diff --git a/.changeset/orange-beers-draw.md b/.changeset/orange-beers-draw.md deleted file mode 100644 index 58ceec630a0..00000000000 --- a/.changeset/orange-beers-draw.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@iota/bcs': patch -'@iota/create-dapp': patch -'@iota/dapp-kit': patch -'@iota/graphql-transport': patch -'@iota/kiosk': patch -'@iota/ledgerjs-hw-app-iota': patch -'@iota/iota-sdk': patch -'@iota/wallet-standard': patch ---- - -First public release. diff --git a/.github/crates-filters.yml b/.github/crates-filters.yml index 55ca74cc759..57a47dd7a25 100644 --- a/.github/crates-filters.yml +++ b/.github/crates-filters.yml @@ -180,9 +180,3 @@ iota-move-natives-latest: - "iota-execution/latest/iota-move-natives/**" iota-verifier-latest: - "iota-execution/latest/iota-verifier/**" -iota-adapter-v0: - - "iota-execution/v0/iota-adapter/**" -iota-move-natives-v0: - - "iota-execution/v0/iota-move-natives/**" -iota-verifier-v0: - - "iota-execution/v0/iota-verifier/**" diff --git a/.github/workflows/_e2e.yml b/.github/workflows/_e2e.yml index 6438470f121..42eb66e2c64 100644 --- a/.github/workflows/_e2e.yml +++ b/.github/workflows/_e2e.yml @@ -9,6 +9,9 @@ on: isExplorer: type: boolean required: true + isAppsBackend: + type: boolean + required: true isTypescriptSDK: type: boolean required: true @@ -74,6 +77,14 @@ jobs: if: inputs.isGraphQlTransport || inputs.isRust || inputs.isDevelop run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @iota/graphql-transport test:e2e' + - name: Build apps-backend + if: inputs.isAppsBackend || inputs.isDevelop + run: pnpm --filter apps-backend build + + - name: Run apps-backend e2e tests + if: inputs.isAppsBackend || inputs.isDevelop + run: pnpm --filter apps-backend test:e2e + - name: Build explorer if: inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRust || inputs.isDevelop run: pnpm turbo --filter=iota-explorer build diff --git a/.github/workflows/hierarchy.yml b/.github/workflows/hierarchy.yml index 47d92c4ce92..612d35b03a0 100644 --- a/.github/workflows/hierarchy.yml +++ b/.github/workflows/hierarchy.yml @@ -145,6 +145,7 @@ jobs: isRust: ${{ needs.diff.outputs.isRust == 'true' }} isWallet: ${{ needs.diff.outputs.isWallet == 'true' }} isExplorer: ${{ needs.diff.outputs.isExplorer == 'true' }} + isAppsBackend: ${{ needs.diff.outputs.isAppsBackend == 'true' }} isTypescriptSDK: ${{ needs.diff.outputs.isTypescriptSDK == 'true' }} isGraphQlTransport: ${{ needs.diff.outputs.isGraphQlTransport == 'true' }} isDevelop: ${{ github.ref_name == 'develop' }} diff --git a/Cargo.lock b/Cargo.lock index c00578c507c..e598cc0b441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5794,33 +5794,6 @@ dependencies = [ "iota-transactional-test-runner", ] -[[package]] -name = "iota-adapter-v0" -version = "0.1.0" -dependencies = [ - "anyhow", - "bcs", - "iota-macros", - "iota-metrics", - "iota-move-natives-v0", - "iota-protocol-config", - "iota-types", - "iota-verifier-v0", - "leb128", - "move-binary-format", - "move-bytecode-utils", - "move-bytecode-verifier-meter", - "move-bytecode-verifier-v0", - "move-core-types", - "move-vm-config", - "move-vm-profiler", - "move-vm-runtime-v0", - "move-vm-types", - "parking_lot 0.12.3", - "serde", - "tracing", -] - [[package]] name = "iota-analytics-indexer" version = "0.7.0-alpha" @@ -6479,20 +6452,15 @@ version = "0.1.0" dependencies = [ "cargo_metadata 0.15.4", "iota-adapter-latest", - "iota-adapter-v0", "iota-move-natives-latest", - "iota-move-natives-v0", "iota-protocol-config", "iota-types", "iota-verifier-latest", - "iota-verifier-v0", "move-binary-format", "move-bytecode-verifier", "move-bytecode-verifier-meter", - "move-bytecode-verifier-v0", "move-vm-config", "move-vm-runtime", - "move-vm-runtime-v0", "petgraph 0.6.5", ] @@ -6615,14 +6583,14 @@ dependencies = [ "fastcrypto", "flate2", "fs_extra", - "iota-adapter-v0", + "iota-adapter-latest", "iota-config", "iota-execution", "iota-framework", "iota-framework-snapshot", "iota-genesis-common", "iota-move-build", - "iota-move-natives-v0", + "iota-move-natives-latest", "iota-protocol-config", "iota-sdk 1.1.5", "iota-simulator", @@ -6633,7 +6601,7 @@ dependencies = [ "move-compiler", "move-core-types", "move-package", - "move-vm-runtime-v0", + "move-vm-runtime", "packable", "prefix-hex", "prometheus", @@ -7210,28 +7178,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iota-move-natives-v0" -version = "0.1.0" -dependencies = [ - "bcs", - "better_any", - "fastcrypto", - "fastcrypto-vdf", - "fastcrypto-zkp", - "indexmap 2.5.0", - "iota-protocol-config", - "iota-types", - "move-binary-format", - "move-core-types", - "move-stdlib-natives-v0", - "move-vm-runtime-v0", - "move-vm-types", - "rand 0.8.5", - "smallvec", - "tracing", -] - [[package]] name = "iota-network" version = "0.7.0-alpha" @@ -8305,21 +8251,6 @@ dependencies = [ "iota-transactional-test-runner", ] -[[package]] -name = "iota-verifier-v0" -version = "0.1.0" -dependencies = [ - "iota-protocol-config", - "iota-types", - "move-abstract-interpreter-v0", - "move-abstract-stack", - "move-binary-format", - "move-bytecode-utils", - "move-bytecode-verifier-meter", - "move-core-types", - "move-vm-config", -] - [[package]] name = "iota_stronghold" version = "2.1.0" @@ -9377,14 +9308,6 @@ dependencies = [ "move-bytecode-verifier-meter", ] -[[package]] -name = "move-abstract-interpreter-v0" -version = "0.1.0" -dependencies = [ - "move-binary-format", - "move-bytecode-verifier-meter", -] - [[package]] name = "move-abstract-stack" version = "0.0.1" @@ -9486,20 +9409,6 @@ dependencies = [ "move-vm-config", ] -[[package]] -name = "move-bytecode-verifier-v0" -version = "0.1.0" -dependencies = [ - "move-abstract-interpreter", - "move-abstract-stack", - "move-binary-format", - "move-borrow-graph", - "move-bytecode-verifier-meter", - "move-core-types", - "move-vm-config", - "petgraph 0.5.1", -] - [[package]] name = "move-bytecode-viewer" version = "0.1.0" @@ -9870,20 +9779,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "move-stdlib-natives-v0" -version = "0.1.1" -dependencies = [ - "hex", - "move-binary-format", - "move-core-types", - "move-vm-runtime-v0", - "move-vm-types", - "sha2 0.9.9", - "sha3 0.9.1", - "smallvec", -] - [[package]] name = "move-symbol-pool" version = "0.1.0" @@ -9988,24 +9883,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "move-vm-runtime-v0" -version = "0.1.0" -dependencies = [ - "better_any", - "fail", - "move-binary-format", - "move-bytecode-verifier-v0", - "move-core-types", - "move-vm-config", - "move-vm-profiler", - "move-vm-types", - "once_cell", - "parking_lot 0.11.2", - "smallvec", - "tracing", -] - [[package]] name = "move-vm-test-utils" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0a0ff6300d9..f531d7a4b54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,11 +53,6 @@ exclude = [ "external-crates/move/crates/move-vm-types", "external-crates/move/crates/serializer-tests", "external-crates/move/crates/test-generation", - "external-crates/move/move-execution/v0/crates/bytecode-verifier-tests", - "external-crates/move/move-execution/v0/crates/move-abstract-interpreter", - "external-crates/move/move-execution/v0/crates/move-bytecode-verifier", - "external-crates/move/move-execution/v0/crates/move-stdlib-natives", - "external-crates/move/move-execution/v0/crates/move-vm-runtime", "sdk/move-bytecode-template", ] members = [ @@ -162,9 +157,6 @@ members = [ "iota-execution/latest/iota-adapter", "iota-execution/latest/iota-move-natives", "iota-execution/latest/iota-verifier", - "iota-execution/v0/iota-adapter", - "iota-execution/v0/iota-move-natives", - "iota-execution/v0/iota-verifier", ] [workspace.package] diff --git a/apps/apps-backend/jest-e2e.json b/apps/apps-backend/jest-e2e.json new file mode 100644 index 00000000000..8a02f0e8a41 --- /dev/null +++ b/apps/apps-backend/jest-e2e.json @@ -0,0 +1,13 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": "./", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "testEnvironment": "node", + "moduleNameMapper": { + "^src/(.*)$": "/dist/$1", + "^@iota/core/(.*)$": "/dist/core/src/$1" + } +} diff --git a/apps/apps-backend/package.json b/apps/apps-backend/package.json index eb94045a4f5..149cc6fd9fc 100644 --- a/apps/apps-backend/package.json +++ b/apps/apps-backend/package.json @@ -6,8 +6,9 @@ "format": "prettier --write \"src/**/*.ts\"", "dev": "nest start --watch", "debug": "nest start --debug --watch", - "preview": "pnpm run build && node dist/main", - "lint": "eslint --max-warnings=0 \"{src,apps,libs,test}/**/*.ts\"" + "preview": "pnpm run build && node dist/apps-backend/src/main", + "lint": "eslint --max-warnings=0 \"{src,apps,libs,test}/**/*.ts\"", + "test:e2e": "jest --config ./jest-e2e.json" }, "dependencies": { "@iota/core": "workspace:*", diff --git a/apps/apps-backend/src/app.module.ts b/apps/apps-backend/src/app.module.ts index 0dc2844e50e..b7fd88272e7 100644 --- a/apps/apps-backend/src/app.module.ts +++ b/apps/apps-backend/src/app.module.ts @@ -11,6 +11,7 @@ import { FeaturesModule } from './features/features.module'; import { MonitorNetworkModule } from './monitor-network/monitor-network.module'; import { PricesModule } from './prices/prices.module'; import { RestrictedModule } from './restricted/restricted.module'; +import { HealthModule } from './health/health.module'; @Module({ imports: [ @@ -30,6 +31,7 @@ import { RestrictedModule } from './restricted/restricted.module'; ttl: 3600, max: 100, }), + HealthModule, ], }) export class AppModule {} diff --git a/apps/apps-backend/src/health/health.controller.ts b/apps/apps-backend/src/health/health.controller.ts new file mode 100644 index 00000000000..c86fdd5fc40 --- /dev/null +++ b/apps/apps-backend/src/health/health.controller.ts @@ -0,0 +1,12 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Controller, Get } from '@nestjs/common'; + +@Controller('health') +export class HealthController { + @Get() + check() { + return { status: 'ok' }; + } +} diff --git a/apps/apps-backend/src/health/health.module.ts b/apps/apps-backend/src/health/health.module.ts new file mode 100644 index 00000000000..e6394484ac5 --- /dev/null +++ b/apps/apps-backend/src/health/health.module.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Module } from '@nestjs/common'; +import { HealthController } from './health.controller'; + +@Module({ + controllers: [HealthController], +}) +export class HealthModule {} diff --git a/apps/apps-backend/test/health.e2e-spec.ts b/apps/apps-backend/test/health.e2e-spec.ts new file mode 100644 index 00000000000..f8ea44ed7cc --- /dev/null +++ b/apps/apps-backend/test/health.e2e-spec.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Health Check (e2e)', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('/health (GET)', async () => { + await request(app.getHttpServer()).get('/health').expect(200).expect({ status: 'ok' }); + }); +}); diff --git a/apps/wallet/package.json b/apps/wallet/package.json index 06e3d0ec6d4..e5eb70e5e54 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -1,6 +1,6 @@ { "name": "iota-wallet", - "version": "0.1.0", + "version": "0.3.0", "private": true, "description": "A wallet for IOTA", "main": "./dist/ui.js", diff --git a/apps/wallet/src/manifest/manifest.json b/apps/wallet/src/manifest/manifest.json index 944f62bba9b..f768672d907 100644 --- a/apps/wallet/src/manifest/manifest.json +++ b/apps/wallet/src/manifest/manifest.json @@ -10,7 +10,7 @@ "action": { "default_popup": "ui.html?type=popup" }, - "host_permissions": ["https://fullnode.devnet.iota.org/", "https://fullnode.staging.iota.org/"], + "host_permissions": ["https://api.devnet.iota.cafe/", "https://api.testnet.iota.cafe/"], "icons": { "16": "manifest/icons/iota-icon-16.png", "32": "manifest/icons/iota-icon-32.png", diff --git a/apps/wallet/src/shared/constants.ts b/apps/wallet/src/shared/constants.ts index dd930448773..61f24fe3181 100644 --- a/apps/wallet/src/shared/constants.ts +++ b/apps/wallet/src/shared/constants.ts @@ -2,9 +2,8 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export const ToS_LINK = 'https://www.iota.org/terms-of-use'; -export const PRIVACY_POLICY_LINK = 'https://www.iota.org/privacy-policy'; -export const FAQ_LINK = 'https://wiki.iota.org/'; +export const ToS_LINK = 'https://www.iota.org/iota-wallet-tos'; +export const FAQ_LINK = 'https://docs.iota.org/about-iota/iota-wallet/FAQ '; // number of epochs before earning // Staking Rewards Redeemable diff --git a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx index ddaafb3af36..00792a40e54 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx @@ -58,11 +58,12 @@ function NFTDetailsPage() { const isPending = isNftLoading || isPendingDisplay || isGuardLoading; function handleMoreAboutKiosk() { - window.open('https://wiki.iota.org/', '_blank'); + window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); } function handleMarketplace() { - window.open('https://wiki.iota.org/', '_blank'); + // TODO: https://github.com/iotaledger/iota/issues/4024 + window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); } function handleSend() { diff --git a/crates/iota-archival/Cargo.toml b/crates/iota-archival/Cargo.toml index 4b0e33368ef..20ff23faa22 100644 --- a/crates/iota-archival/Cargo.toml +++ b/crates/iota-archival/Cargo.toml @@ -31,7 +31,7 @@ iota-types = { workspace = true, features = ["test-utils"] } [dev-dependencies] # external dependencies ed25519-consensus.workspace = true -fastcrypto = { workspace = true } +fastcrypto.workspace = true more-asserts.workspace = true tempfile.workspace = true tokio = { workspace = true, features = ["test-util"] } diff --git a/crates/iota-genesis-builder/Cargo.toml b/crates/iota-genesis-builder/Cargo.toml index a70eac62788..81d3c77ffe1 100644 --- a/crates/iota-genesis-builder/Cargo.toml +++ b/crates/iota-genesis-builder/Cargo.toml @@ -40,14 +40,14 @@ tracing.workspace = true tracing-subscriber = "0.3" # internal dependencies -iota-adapter-v0 = { path = "../../iota-execution/v0/iota-adapter/" } +iota-adapter-latest = { path = "../../iota-execution/latest/iota-adapter/" } iota-config.workspace = true iota-execution.workspace = true iota-framework.workspace = true iota-framework-snapshot.workspace = true iota-genesis-common.workspace = true iota-move-build.workspace = true -iota-move-natives-v0 = { path = "../../iota-execution/v0/iota-move-natives" } +iota-move-natives-latest = { path = "../../iota-execution/latest/iota-move-natives" } iota-protocol-config.workspace = true iota-sdk = { version = "1.1", default-features = false, features = ["irc_27", "irc_30", "std"] } iota-types.workspace = true @@ -55,7 +55,7 @@ move-binary-format.workspace = true move-compiler.workspace = true move-core-types.workspace = true move-package.workspace = true -move-vm-runtime-v0 = { path = "../../external-crates/move/move-execution/v0/crates/move-vm-runtime" } +move-vm-runtime-latest = { path = "../../external-crates/move/crates/move-vm-runtime", package = "move-vm-runtime" } shared-crypto.workspace = true [target.'cfg(msim)'.dependencies] diff --git a/crates/iota-genesis-builder/src/stardust/migration/executor.rs b/crates/iota-genesis-builder/src/stardust/migration/executor.rs index 8c7b6fb96f6..816123dba63 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/executor.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/executor.rs @@ -7,13 +7,13 @@ use std::{ }; use anyhow::Result; -use iota_adapter_v0::{ +use iota_adapter_latest::{ adapter::new_move_vm, execution_mode, gas_charger::GasCharger, programmable_transactions, temporary_store::TemporaryStore, }; use iota_framework::BuiltInFramework; use iota_move_build::CompiledPackage; -use iota_move_natives_v0::all_natives; +use iota_move_natives_latest::all_natives; use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion}; use iota_sdk::types::block::output::{ AliasOutput, BasicOutput, FoundryOutput, NativeTokens, NftOutput, OutputId, TokenId, @@ -44,7 +44,7 @@ use iota_types::{ }, }; use move_core_types::{ident_str, language_storage::StructTag}; -use move_vm_runtime_v0::move_vm::MoveVM; +use move_vm_runtime_latest::move_vm::MoveVM; use crate::{ process_package, diff --git a/crates/iota-open-rpc/Cargo.toml b/crates/iota-open-rpc/Cargo.toml index 4c09e4be5f6..daeb6c88236 100644 --- a/crates/iota-open-rpc/Cargo.toml +++ b/crates/iota-open-rpc/Cargo.toml @@ -20,7 +20,7 @@ versions = "4.1.0" anyhow.workspace = true bcs.workspace = true clap.workspace = true -fastcrypto = { workspace = true } +fastcrypto.workspace = true pretty_assertions.workspace = true rand.workspace = true tokio = { workspace = true, features = ["full"] } diff --git a/crates/iota-sdk/Cargo.toml b/crates/iota-sdk/Cargo.toml index ed15f8733a8..f65de0938da 100644 --- a/crates/iota-sdk/Cargo.toml +++ b/crates/iota-sdk/Cargo.toml @@ -75,8 +75,8 @@ path = "examples/json_rpc_errors.rs" name = "utils" path = "examples/utils.rs" crate-type = ["staticlib"] -# Read API folder +# Read API folder [[example]] name = "checkpoints" path = "examples/read_api/checkpoints.rs" diff --git a/crates/iota-sdk/README.md b/crates/iota-sdk/README.md index 21ae42ca4ea..bb3ca4e99dc 100644 --- a/crates/iota-sdk/README.md +++ b/crates/iota-sdk/README.md @@ -19,11 +19,11 @@ use iota_sdk::IotaClientBuilder; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - // Iota testnet -- https://fullnode.testnet.iota.io:443 + // Iota testnet -- https://api.testnet.iota.cafe let iota_testnet = IotaClientBuilder::default().build_testnet().await?; println!("Iota testnet version: {}", iota_testnet.api_version()); - // Iota devnet -- https://fullnode.devnet.iota.io:443 + // Iota devnet -- https://api.devnet.iota.cafe let iota_devnet = IotaClientBuilder::default().build_devnet().await?; println!("Iota devnet version: {}", iota_devnet.api_version()); @@ -53,13 +53,13 @@ There are several files ending in `_api.rs` which provide code examples of the c ### Prerequisites -Unless otherwise specified, most of these examples assume `Rust` and `cargo` are installed, and that there is an available internet connection. The examples connect to the IOTA testnet (`https://fullnode.testnet.iota.io:443`) and execute different APIs using the active address from the local wallet. If there is no local wallet, it will create one, generate two addresses, set one of them to be active, and it will request 1 IOTA from the testnet faucet for the active address. +Unless otherwise specified, most of these examples assume `Rust` and `cargo` are installed, and that there is an available internet connection. The examples connect to the IOTA testnet (`https://api.testnet.iota.cafe`) and execute different APIs using the active address from the local wallet. If there is no local wallet, it will create one, generate two addresses, set one of them to be active, and it will request 1 IOTA from the testnet faucet for the active address. ### Running the existing examples In the root folder of the `iota` repository (or in the `iota-sdk` crate folder), you can individually run examples using the command `cargo run --example filename` (without `.rs` extension). For example: -- `cargo run --example iota_client` -- this one requires a local Iota network running (see [#Connecting to Iota Network](https://wiki.iota.cafe/developer/getting-started/local-network#start-the-local-network). If you do not have a local Iota network running, please skip this example. +- `cargo run --example iota_client` -- this one requires a local Iota network running (see [#Connecting to Iota Network](https://docs.iota.org/developer/getting-started/local-network#start-a-local-network). If you do not have a local Iota network running, please skip this example. - `cargo run --example coin_read_api` - `cargo run --example event_api` -- note that this will subscribe to a stream and thus the program will not terminate unless forced (Ctrl+C) - `cargo run --example governance_api` @@ -74,13 +74,13 @@ In the root folder of the `iota` repository (or in the `iota-sdk` crate folder), The `IotaClientBuilder` struct provides a connection to the JSON-RPC server that you use for all read-only operations. The default URLs to connect to the IOTA network are: - Local: http://127.0.0.1:9000 -- Devnet: https://fullnode.devnet.iota.io:443 -- Testnet: https://fullnode.testnet.iota.io:443 -- Mainnet: https://fullnode.mainnet.iota.io:443 +- Devnet: https://api.devnet.iota.cafe +- Testnet: https://api.testnet.iota.cafe +- Mainnet: https://api.mainnet.iota.cafe -For all available servers, see [here](TODO: https://github.com/iotaledger/iota/issues/1614). +For all available servers, see [here](https://docs.iota.org/developer/network-overview). -For running a local IOTA network, please follow [this guide](https://wiki.iota.cafe/developer/getting-started/iota-install) for installing IOTA and [this guide](https://wiki.iota.cafe/developer/getting-started/local-network#start-the-local-network) for starting the local IOTA network. +For running a local IOTA network, please follow [this guide](https://docs.iota.org/developer/getting-started/install-iota) for installing IOTA and [this guide](https://docs.iota.org/developer/getting-started/local-network#start-a-local-network) for starting the local IOTA network. ```rust use iota_sdk::IotaClientBuilder; @@ -96,11 +96,11 @@ async fn main() -> Result<(), anyhow::Error> { let iota_local = IotaClientBuilder::default().build_localnet().await?; println!("IOTA local network version: {}", iota_local.api_version()); - // IOTA devnet -- https://fullnode.devnet.iota.io:443 + // IOTA devnet -- https://api.devnet.iota.cafe let iota_devnet = IotaClientBuilder::default().build_devnet().await?; println!("IOTA devnet version: {}", iota_devnet.api_version()); - // IOTA testnet -- https://fullnode.testnet.iota.io:443 + // IOTA testnet -- https://api.testnet.iota.cafe let iota_testnet = IotaClientBuilder::default().build_testnet().await?; println!("IOTA testnet version: {}", iota_testnet.api_version()); @@ -141,15 +141,15 @@ See the programmable transactions [example](https://github.com/iotaledger/iota/b 1. Prepare the environment - 1. Install `iota` binary following the [IOTA installation](https://wiki.iota.cafe/developer/getting-started/iota-install) docs. - 1. [Connect to IOTA Devnet](https://wiki.iota.cafe/developer/getting-started/connect). - 1. [Make sure you have two addresses with gas](https://wiki.iota.cafe/developer/getting-started/get-address) by using the `new-address` command to create new addresses: + 1. Install `iota` binary following the [IOTA installation](https://docs.iota.org/developer/getting-started/install-iota) docs. + 1. [Connect to IOTA Devnet](https://docs.iota.org/developer/getting-started/connect). + 1. [Make sure you have two addresses with gas](https://docs.iota.org/developer/getting-started/get-address) by using the `new-address` command to create new addresses: ```shell iota client new-address ed25519 ``` You must specify the key scheme, one of `ed25519` or `secp256k1` or `secp256r1`. You can skip this step if you are going to play with a friend. :) - 1. [Request IOTA tokens](https://wiki.iota.cafe/developer/getting-started/get-coins) for all addresses that will be used to join the game. + 1. [Request IOTA tokens](https://docs.iota.org/developer/getting-started/get-coins) for all addresses that will be used to join the game. 2. Publish the move contract diff --git a/crates/iota-sdk/examples/coin_read_api.rs b/crates/iota-sdk/examples/coin_read_api.rs index 261423aea44..f82c975af69 100644 --- a/crates/iota-sdk/examples/coin_read_api.rs +++ b/crates/iota-sdk/examples/coin_read_api.rs @@ -8,8 +8,7 @@ //! doesn't) check if it has coins and request coins from the faucet if there //! aren't any. If there is no wallet, it will create a wallet and two //! addresses, set one address as active, and add 1 IOTA to the active address. -//! By default, the example will use the Iota testnet network (fullnode.testnet. -//! iota.io:443). +//! By default, the example will use the IOTA testnet network (https://api.testnet.iota.cafe). //! //! cargo run --example coin_read_api diff --git a/crates/iota-sdk/examples/event_api.rs b/crates/iota-sdk/examples/event_api.rs index 07517815c3a..b843ec0b101 100644 --- a/crates/iota-sdk/examples/event_api.rs +++ b/crates/iota-sdk/examples/event_api.rs @@ -36,8 +36,8 @@ async fn main() -> Result<(), anyhow::Error> { println!(" *** Query events ***\n "); let ws = IotaClientBuilder::default() - .ws_url("wss://rpc.testnet.iota.io:443") - .build("https://fullnode.testnet.iota.io:443") + .ws_url("wss://api.testnet.iota.cafe") + .build("https://api.testnet.iota.cafe") .await?; println!("WS version {:?}", ws.api_version()); diff --git a/crates/iota-sdk/examples/iota_client.rs b/crates/iota-sdk/examples/iota_client.rs index 0649ef17ef0..80fca08fe8d 100644 --- a/crates/iota-sdk/examples/iota_client.rs +++ b/crates/iota-sdk/examples/iota_client.rs @@ -2,13 +2,13 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! This example shows the few basic ways to connect to an Iota network. -//! There are several in-built methods for connecting to the Iota devnet, +//! This example shows the few basic ways to connect to an IOTA network. +//! There are several in-built methods for connecting to the IOTA devnet, //! testnet, and localnet (running locally), as well as a custom way for //! connecting to custom URLs. The example prints out the API versions of the //! different networks, and finally, it prints the list of available RPC methods //! and the list of subscriptions. Note that running this code will fail if -//! there is no Iota network running locally on the default address: +//! there is no IOTA network running locally on the default address: //! 127.0.0.1:9000 //! //! cargo run --example iota_client @@ -20,19 +20,19 @@ async fn main() -> Result<(), anyhow::Error> { let client = IotaClientBuilder::default() .build("http://127.0.0.1:9000") // local network address .await?; - println!("Iota local network version: {}", client.api_version()); + println!("IOTA local network version: {}", client.api_version()); - // local Iota network, like the above one but using the dedicated function + // local IOTA network, like the above one but using the dedicated function let local_client = IotaClientBuilder::default().build_localnet().await?; - println!("Iota local network version: {}", local_client.api_version()); + println!("IOTA local network version: {}", local_client.api_version()); - // Iota devnet -- https://fullnode.devnet.iota.io:443 + // IOTA devnet -- https://api.devnet.iota.cafe let devnet_client = IotaClientBuilder::default().build_devnet().await?; println!("Iota devnet version: {}", devnet_client.api_version()); - // Iota testnet -- https://fullnode.testnet.iota.io:443 + // IOTA testnet -- https://api.testnet.iota.cafe let testnet_client = IotaClientBuilder::default().build_testnet().await?; - println!("Iota testnet version: {}", testnet_client.api_version()); + println!("IOTA testnet version: {}", testnet_client.api_version()); println!("{:?}", local_client.available_rpc_methods()); println!("{:?}", local_client.available_subscriptions()); diff --git a/crates/iota-sdk/examples/read_api/transaction_subscription.rs b/crates/iota-sdk/examples/read_api/transaction_subscription.rs index 2ff3cefa46f..44bdadcfe2b 100644 --- a/crates/iota-sdk/examples/read_api/transaction_subscription.rs +++ b/crates/iota-sdk/examples/read_api/transaction_subscription.rs @@ -16,8 +16,8 @@ use iota_sdk::IotaClientBuilder; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let client = IotaClientBuilder::default() - .ws_url("wss://rpc.testnet.iota.io:443") - .build("https://fullnode.testnet.iota.io:443") + .ws_url("wss://api.testnet.iota.cafe") + .build("https://api.testnet.iota.cafe") .await?; println!("WS version {:?}", client.api_version()); diff --git a/crates/iota-sdk/examples/transaction_builder/function_move_call.rs b/crates/iota-sdk/examples/transaction_builder/function_move_call.rs index 46ac42092ee..f26d326828e 100644 --- a/crates/iota-sdk/examples/transaction_builder/function_move_call.rs +++ b/crates/iota-sdk/examples/transaction_builder/function_move_call.rs @@ -15,9 +15,7 @@ use iota_sdk::{ base_types::ObjectID, programmable_transaction_builder::ProgrammableTransactionBuilder, quorum_driver_types::ExecuteTransactionRequestType, - transaction::{ - Argument, CallArg, Command, ProgrammableMoveCall, Transaction, TransactionData, - }, + transaction::{Argument, CallArg, Command, Transaction, TransactionData}, }, }; use shared_crypto::intent::Intent; @@ -66,13 +64,9 @@ async fn main() -> Result<(), anyhow::Error> { let package = ObjectID::from_hex_literal(pkg_id).map_err(|e| anyhow!(e))?; let module = Identifier::new("hello_world").map_err(|e| anyhow!(e))?; let function = Identifier::new("hello_world").map_err(|e| anyhow!(e))?; - ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { - package, - module, - function, - type_arguments: vec![], - arguments: vec![Argument::Input(0)], - }))); + ptb.command(Command::move_call(package, module, function, vec![], vec![ + Argument::Input(0), + ])); // build the transaction block by calling finish on the ptb let builder = ptb.finish(); diff --git a/crates/iota-sdk/examples/utils.rs b/crates/iota-sdk/examples/utils.rs index 02356041cff..8c6b914143b 100644 --- a/crates/iota-sdk/examples/utils.rs +++ b/crates/iota-sdk/examples/utils.rs @@ -38,9 +38,9 @@ struct FaucetResponse { error: Option, } -// const IOTA_FAUCET_BASE_URL: &str = "https://faucet.devnet.iota.io"; // devnet faucet +// const IOTA_FAUCET_BASE_URL: &str = "https://faucet.devnet.iota.cafe"; // devnet faucet -pub const IOTA_FAUCET_BASE_URL: &str = "https://faucet.testnet.iota.io"; // testnet faucet +pub const IOTA_FAUCET_BASE_URL: &str = "https://faucet.testnet.iota.cafe"; // testnet faucet // if you use the `iota start` subcommand and use the local network; if it does // not work, try with port 5003. const IOTA_FAUCET_BASE_URL: &str = "http://127.0.0.1:9123"; diff --git a/crates/iota-sdk/src/apis/event.rs b/crates/iota-sdk/src/apis/event.rs index 17383a103c6..516d0fe9539 100644 --- a/crates/iota-sdk/src/apis/event.rs +++ b/crates/iota-sdk/src/apis/event.rs @@ -45,8 +45,8 @@ impl EventApi { /// #[tokio::main] /// async fn main() -> Result<(), anyhow::Error> { /// let iota = IotaClientBuilder::default() - /// .ws_url("wss://rpc.mainnet.iota.io:443") - /// .build("https://fullnode.mainnet.iota.io:443") + /// .ws_url("wss://api.mainnet.iota.cafe") + /// .build("https://api.mainnet.iota.cafe") /// .await?; /// let mut subscribe_all = iota /// .event_api() diff --git a/crates/iota-sdk/src/lib.rs b/crates/iota-sdk/src/lib.rs index e35306a3824..04188ce9f7b 100644 --- a/crates/iota-sdk/src/lib.rs +++ b/crates/iota-sdk/src/lib.rs @@ -2,11 +2,11 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! The Iota Rust SDK +//! The IOTA Rust SDK //! //! It aims at providing a similar SDK functionality like the one existing for //! [TypeScript](https://github.com/iotaledger/iota/tree/main/sdk/typescript/). -//! Iota Rust SDK builds on top of the [JSON RPC API](https://docs.iota.io/iota-jsonrpc) +//! IOTA Rust SDK builds on top of the [JSON RPC API](https://docs.iota.org/iota-api-ref) //! and therefore many of the return types are the ones specified in //! [iota_types]. //! diff --git a/crates/iota-storage/Cargo.toml b/crates/iota-storage/Cargo.toml index d0d7a9cb32b..bc6d3a66e96 100644 --- a/crates/iota-storage/Cargo.toml +++ b/crates/iota-storage/Cargo.toml @@ -63,7 +63,7 @@ once_cell.workspace = true tempfile.workspace = true # internal dependencies -iota-macros = { workspace = true } +iota-macros.workspace = true iota-test-transaction-builder.workspace = true iota-types = { workspace = true, features = ["test-utils"] } diff --git a/crates/iota-tool/src/commands.rs b/crates/iota-tool/src/commands.rs index 7c4a03eabcb..18416b2401f 100644 --- a/crates/iota-tool/src/commands.rs +++ b/crates/iota-tool/src/commands.rs @@ -708,9 +708,9 @@ impl ToolCommand { let aws_endpoint = env::var("AWS_SNAPSHOT_ENDPOINT").ok().or_else(|| { if no_sign_request { if network == Chain::Mainnet { - Some("https://formal-snapshot.mainnet.iota.io".to_string()) + Some("https://dbfiles.mainnet.iota.cafe/formal".to_string()) } else if network == Chain::Testnet { - Some("https://formal-snapshot.testnet.iota.io".to_string()) + Some("https://dbfiles.testnet.iota.cafe/formal".to_string()) } else { None } @@ -941,9 +941,9 @@ impl ToolCommand { let snapshot_store_config = if no_sign_request { let aws_endpoint = env::var("AWS_SNAPSHOT_ENDPOINT").ok().or_else(|| { if network == Chain::Mainnet { - Some("https://db-snapshot.mainnet.iota.io".to_string()) + Some("https://dbfiles.mainnet.iota.cafe/snapshots".to_string()) } else if network == Chain::Testnet { - Some("https://db-snapshot.testnet.iota.io".to_string()) + Some("https://dbfiles.testnet.iota.cafe/snapshots".to_string()) } else { None } diff --git a/crates/iota-util-mem/Cargo.toml b/crates/iota-util-mem/Cargo.toml index 3aa8608f96d..65acba595c3 100644 --- a/crates/iota-util-mem/Cargo.toml +++ b/crates/iota-util-mem/Cargo.toml @@ -12,7 +12,7 @@ description = "Collection of memory related utilities" # external dependencies cfg-if.workspace = true ed25519-consensus.workspace = true -fastcrypto = { workspace = true } +fastcrypto.workspace = true fastcrypto-tbls.workspace = true hashbrown = { version = "0.12", optional = true } impl-trait-for-tuples = "0.2.0" diff --git a/crates/iota/src/iota_commands.rs b/crates/iota/src/iota_commands.rs index ea99709667a..0e37b2f016b 100644 --- a/crates/iota/src/iota_commands.rs +++ b/crates/iota/src/iota_commands.rs @@ -164,11 +164,11 @@ pub enum IotaCommand { #[clap(name = "start")] Start { /// Config directory that will be used to store network config, node db, - /// keystore iota genesis -f --with-faucet generates a genesis - /// config that can be used to start this process. Use with - /// caution as the `-f` flag will overwrite the existing config - /// directory. We can use any config dir that is generated by - /// the `iota genesis`. + /// keystore. + /// `iota genesis -f --with-faucet` generates a genesis config that can + /// be used to start this process. Use with caution as the `-f` flag + /// will overwrite the existing config directory. We can use any config + /// dir that is generated by the `iota genesis`. #[clap(long = "network.config")] config_dir: Option, @@ -527,7 +527,7 @@ impl IotaCommand { PersistedConfig::read(&bridge_committee_config_path).map_err(|err| { err.context(format!( "Cannot open Bridge Committee config file at {:?}", - network_config_path + bridge_committee_config_path )) })?; diff --git a/crates/iota/src/zklogin_commands_util.rs b/crates/iota/src/zklogin_commands_util.rs index 57a3f393c6c..5465734e205 100644 --- a/crates/iota/src/zklogin_commands_util.rs +++ b/crates/iota/src/zklogin_commands_util.rs @@ -217,10 +217,10 @@ pub async fn perform_zk_login_test_tx( fn get_config(network: &str) -> (&str, &str) { match network { "devnet" => ( - "https://faucet.devnet.iota.io/gas", - "https://rpc.devnet.iota.io:443", + "https://faucet.devnet.iota.cafe/v1/gas", + "https://api.devnet.iota.cafe", ), - "localnet" => ("http://127.0.0.1:9123/gas", "http://127.0.0.1:9000"), + "localnet" => ("http://127.0.0.1:9123/v1/gas", "http://127.0.0.1:9000"), _ => panic!("Invalid network"), } } diff --git a/dapps/kiosk/src/components/Kiosk/KioskCreation.tsx b/dapps/kiosk/src/components/Kiosk/KioskCreation.tsx index 1248e4388fa..f90b06c5d02 100644 --- a/dapps/kiosk/src/components/Kiosk/KioskCreation.tsx +++ b/dapps/kiosk/src/components/Kiosk/KioskCreation.tsx @@ -29,7 +29,7 @@ export function KioskCreation({ onCreate }: { onCreate: () => void }) { wallet connects to Testnet and that you have at least 1 IOTA to cover gas fees. You can get test IOTA tokens using{' '} IOTA MultiSig Address - https://wiki.iota.org/testnet/learn/cryptography/iota-multisig + https://docs.iota.org/developer/cryptography/transaction-auth/multisig @@ -249,7 +249,7 @@ export default function MultiSigCombineSignatureGenerator() { IOTA MultiSig Combined Address - https://wiki.iota.org/testnet/learn/cryptography/iota-multisig + https://docs.iota.org/developer/cryptography/transaction-auth/multisig diff --git a/dapps/multisig-toolkit/src/routes/multisig-address.tsx b/dapps/multisig-toolkit/src/routes/multisig-address.tsx index 9dcea4b9863..9d7759cdd19 100644 --- a/dapps/multisig-toolkit/src/routes/multisig-address.tsx +++ b/dapps/multisig-toolkit/src/routes/multisig-address.tsx @@ -137,7 +137,7 @@ export default function MultiSigAddressGenerator() { IOTA MultiSig Address - https://wiki.iota.org/testnet/learn/cryptography/iota-multisig + https://docs.iota.org/references/ts-sdk/typescript/cryptography/multisig diff --git a/docs/README.md b/docs/README.md index 0296c0dffdb..63ade6b76f9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ ## Overview -This directory contains the assets required to build and update the [IOTA documentation](https://wiki.iota.org). The directory is split between `content` and `site`. To run the site locally, open the root directory in a terminal or console. Use a package manager to install the required modules: +This directory contains the assets required to build and update the [IOTA documentation](https://docs.iota.org). The directory is split between `content` and `site`. To run the site locally, open the root directory in a terminal or console. Use a package manager to install the required modules: ```shell pnpm install @@ -38,11 +38,10 @@ If you'd like to view the Vercel preview before your changes are ready for revie IOTA is for the community. Contribute for the benefit of all. -- [Docs contributing guidelines](https://wiki.iota.cafe/references/contribute/contribution-process) -- [Repo contributing guidelines](https://wiki.iota.cafe/contribute-to-iota-repos) -- [Style guide](https://wiki.iota.cafe/style-guide) -- [Localization](https://wiki.iota.cafe/localize-iota-docs) -- [Code of conduct](https://wiki.iota.cafe/code-of-conduct) +- [Docs contributing guidelines](https://docs.iota.org/references/contribute/contribution-process) +- [Repo contributing guidelines](https://github.com/iotaledger/iota/blob/develop/CONTRIBUTING.md) +- [Style guide](https://docs.iota.org/style-guide) +- [Code of conduct](https://docs.iota.org/code-of-conduct) ## License diff --git a/docs/content/_snippets/alphanet.mdx b/docs/content/_snippets/alphanet.mdx deleted file mode 100644 index 7ec499e0560..00000000000 --- a/docs/content/_snippets/alphanet.mdx +++ /dev/null @@ -1,9 +0,0 @@ -:::tip Alphanet - -At the moment, you can only use the Alphanet API, faucet and [explorer](https://explorer.iota.cafe/): - -* **JSON RPC URL**: `https://api.iota-rebased-alphanet.iota.cafe:443` -* **Faucet URL**: `https://faucet.iota-rebased-alphanet.iota.cafe/gas` -* **Explorer**: https://explorer.iota.cafe/?network=alphanet - -::: \ No newline at end of file diff --git a/docs/content/_snippets/mainnet-runs-stardust.mdx b/docs/content/_snippets/mainnet-runs-stardust.mdx new file mode 100644 index 00000000000..39032335d81 --- /dev/null +++ b/docs/content/_snippets/mainnet-runs-stardust.mdx @@ -0,0 +1,5 @@ +:::warning Stardust Protocol + +The IOTA Mainnet runs the [Stardust Protocol](https://wiki.iota.org/learn/protocols/stardust/introduction/). + +::: diff --git a/docs/content/_snippets/not-available-on-testnet.mdx b/docs/content/_snippets/not-available-on-testnet.mdx new file mode 100644 index 00000000000..3d1956f3f15 --- /dev/null +++ b/docs/content/_snippets/not-available-on-testnet.mdx @@ -0,0 +1,5 @@ +:::info Mainnet Only + +IOTA EVM and IOTA Identity are not available on the IOTA Move Testnet. + +::: \ No newline at end of file diff --git a/docs/content/_snippets/warn-ml-rpcs.mdx b/docs/content/_snippets/warn-ml-rpcs.mdx index 312b7266c38..d54a7bd25aa 100644 --- a/docs/content/_snippets/warn-ml-rpcs.mdx +++ b/docs/content/_snippets/warn-ml-rpcs.mdx @@ -1,6 +1,8 @@ :::caution -Use dedicated nodes/shared services rather than public endpoints for production apps. The public endpoints maintained by the IOTA Foundation (`https://api.iota-rebased-alphanet.iota.cafe:443`) are rate-limited, and support only 100 requests per 30 seconds. Do not use public endpoints in production applications with high traffic volume. +Use dedicated nodes/shared services rather than public endpoints for production apps. The public endpoints maintained +by the IOTA Foundation are rate-limited, and support only 100 requests +per 30 seconds. Do not use public endpoints in production applications with high traffic volume. You can either run your own Full nodes, or outsource this to a professional infrastructure provider (preferred for apps that have high traffic). diff --git a/docs/content/about-iota/about-iota.mdx b/docs/content/about-iota/about-iota.mdx index 1ffb6acb93e..8909079db5a 100644 --- a/docs/content/about-iota/about-iota.mdx +++ b/docs/content/about-iota/about-iota.mdx @@ -1,5 +1,7 @@ -# About IOTA +import NotAvailableOnTestnet from '../_snippets/not-available-on-testnet.mdx'; +import MainnetRunsStardust from '../_snippets/mainnet-runs-stardust.mdx'; +# About IOTA The IOTA ecosystem enables programmability in both [Layer 1](#layer-1) and [Layer 2](#layer-2) using [Move](#move) and [EVM/Solidity](#evm) smart contracts respectively. @@ -31,7 +33,10 @@ While the Move compiler catches many development mistakes, smart contracts can s #### IOTA Mainnet -The IOTA Mainnet processes production transaction blocks. The IOTA Mainnet's tokens have a real-world value, so we recommend that you use the IOTA Testnet or Devnet to develop your application. + + +The [IOTA Mainnet](https://wiki.iota.org/build/networks-endpoints/#iota) processes production transaction blocks. The IOTA Mainnet's tokens have a real-world value, so we recommend that you use the IOTA Testnet or Devnet to develop your application. + #### IOTA Testnet @@ -45,11 +50,13 @@ The IOTA Devnet is used to develop new features. You can use this network to cod ### EVM + + [EVM](https://ethereum.org/en/developers/docs/evm/) stands for "Ethereum Virtual Machine" and is currently the tried and tested virtual machine running most smart contract networks. [Solidity](https://soliditylang.org/) is the programming language of choice for the EVM. It was created for this specific purpose. -The main benefit of using EVM/Solidity is its sheer amount of resources from years of development. The [IOTA Smart Contracts](../developer/iota-evm/introduction.mdx) implementation is fully compatible with these resources, allowing you to leverage all existing EVM developer tools for developing on the IOTA EVM. Any contracts you've previously written can be deployed on IOTA Smart Contracts without modification. +The main benefit of using EVM/Solidity is its sheer amount of resources from years of development. The [IOTA Smart Contracts](https://wiki.iota.org/isc/introduction/) implementation is fully compatible with these resources, allowing you to leverage all existing EVM developer tools for developing on the IOTA EVM. Any contracts you've previously written can be deployed on IOTA Smart Contracts without modification. Keep in mind that while EVM has become a standard for smart contract execution on L2s, it is not inherently designed for the unique features and capabilities of L1s like Move smart contracts. @@ -65,6 +72,8 @@ running on top of the [IOTA Mainnet](./tokenomics/iota-token.mdx). #### IOTA EVM Testnet + + IOTA EVM Testnet is the Layer 2 EVM running on top of the IOTA Testnet network. This network is subject to occasional resets (no data retention), usually announced with a one-week grace period. ## IOTA Tokens diff --git a/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx b/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx index ce4ce3e1ccc..c5c4a396447 100644 --- a/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx +++ b/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx @@ -116,7 +116,7 @@ A checkpoint contains a list of transactions and is signed by a majority of vali Before sending back an effects certificate, a full node might execute the transaction locally if the request asks it to. This is more important for high-frequency applications like gaming but can add unnecessary delay for simple transactions like buying coffee. The `WaitForLocalExecution` parameter requests this local execution, while you can use the `WaitForEffects` parameter for a quicker response. -Additionally, when any app builds a transaction, the full node is usually in charge of choosing the object that is used to pay for the transaction's gas. Since gas is paid in IOTA, which is a shared object, if the full node is not up-to-date, it could potentially lead to an invalid transaction or even a [client equivocation](../../developer/iota-101/transactions/sponsored-transactions.mdx#potential-risks-using-sponsored-transactions). You can avoid this unwanted behavior by sending the `WaitForLocalExecution` parameter. +Additionally, when any app builds a transaction, the full node is usually in charge of choosing the object that is used to pay for the transaction's gas. Since gas is paid in IOTA, which is a shared object, if the full node is not up-to-date, it could potentially lead to an invalid transaction or even a [client equivocation](../../developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.mdx#risk-considerations). You can avoid this unwanted behavior by sending the `WaitForLocalExecution` parameter. ### Epoch Change @@ -129,4 +129,5 @@ If your wallet app crashes during a transaction, it stores the signed transactio The wallet app can query the full node with the `getTransactionBlock` method to check if the transaction is finalized. If the response contains transaction details, it is finalized. If the response is `None`, the transaction may need to be resubmitted. This ensures that the coffee shop's full node will eventually recognize the transaction once it's included in a checkpoint and update the coffee shop's balance. ## Quizzes + \ No newline at end of file diff --git a/docs/content/about-iota/iota-wallet/how-to/get-test-tokens.mdx b/docs/content/about-iota/iota-wallet/how-to/get-test-tokens.mdx index b429ef3d240..36690907c32 100644 --- a/docs/content/about-iota/iota-wallet/how-to/get-test-tokens.mdx +++ b/docs/content/about-iota/iota-wallet/how-to/get-test-tokens.mdx @@ -3,7 +3,7 @@ description: A guide on how to use IOTA Wallet for testing on different IOTA net --- # Get Test Tokens -You can test apps on the IOTA network using IOTA Wallet. Besides the IOTA Mainnet, the wallet supports connections to IOTA Testnet, IOTA Devnet, a [Local network](../../../developer/getting-started/local-network.mdx), or a Custom RPC URL for a network you create. +You can test apps on the IOTA network using IOTA Wallet. The wallet supports connections to IOTA Testnet, IOTA Devnet, a [Local network](../../../developer/getting-started/local-network.mdx), or a Custom RPC URL for a network you create. ## Change the Active Network Connection diff --git a/docs/content/developer/advanced/custom-indexer.mdx b/docs/content/developer/advanced/custom-indexer.mdx index d833a92eb9c..3dcabc17a54 100644 --- a/docs/content/developer/advanced/custom-indexer.mdx +++ b/docs/content/developer/advanced/custom-indexer.mdx @@ -31,10 +31,10 @@ Data ingestion for your indexer supports several checkpoint stream sources. The most straightforward stream source is to subscribe to a remote store of checkpoint contents. IOTA provides the following buckets: -- Testnet: `https://checkpoints.testnet.iota.io` -- Mainnet: `https://checkpoints.mainnet.iota.io` +- Testnet: `https://indexer.testnet.iota.cafe` +- Devnet: `https://indexer.devnet.iota.cafe` -The checkpoint files are stored in the following format: `https://checkpoints.testnet.iota.io/.chk`. You can download the checkpoint file by sending an HTTP GET request to the relevant URL. Try it yourself for checkpoint 1 at [https://checkpoints.testnet.iota.io/1.chk](https://checkpoints.testnet.iota.io/1.chk). +The checkpoint files are stored in the following format: `https://indexer.testnet.iota.cafe/.chk`. You can download the checkpoint file by sending an HTTP GET request to the relevant URL. Try it yourself for checkpoint 1 at [https://indexer.testnet.iota.cafe/1.chk](https://indexer.testnet.iota.cafe/1.chk). ```mermaid flowchart LR @@ -110,7 +110,7 @@ Specify both a local and remote store as a fallback to ensure constant data flow ```rust executor.run( PathBuf::from("./chk".to_string()), // path to a local directory - Some("https://checkpoints.testnet.iota.io".to_string()), // Remote Checkpoint Store + Some("https://indexer.testnet.iota.cafe".to_string()), // Remote Checkpoint Store vec![], // optional remote store access options ReaderOptions::default(), exit_receiver, diff --git a/docs/content/developer/advanced/_graphql-migration.mdx b/docs/content/developer/advanced/graphql-migration.mdx similarity index 100% rename from docs/content/developer/advanced/_graphql-migration.mdx rename to docs/content/developer/advanced/graphql-migration.mdx diff --git a/docs/content/developer/developer.mdx b/docs/content/developer/developer.mdx index 49e45c25e13..93c9740fa32 100644 --- a/docs/content/developer/developer.mdx +++ b/docs/content/developer/developer.mdx @@ -2,6 +2,7 @@ title: Developer Information description: Guides and documents for developers on the IOTA network. Whether you're just learning how to create smart contracts or are a Move expert, these guides help to fast-track your IOTA development. --- +import NotAvailableOnTestnet from '../_snippets/not-available-on-testnet.mdx'; The developer section is meant to introduce you to the Move programming language and its implementation on the IOTA network through examples, tasks, and conceptual content. This contains everything a developer needs to get started developing on top of the IOTA network. @@ -65,13 +66,17 @@ Go to the [Developer Cheat Sheet](dev-cheat-sheet.mdx). ## Solidity/EVM Smart contracts + + Everything you need to know about our Layer 2 EVM Support, including working with the IOTA EVM and ShimmerEVM chains. -Go to [EVM Smart Contracts](iota-evm/introduction.mdx). +Go to [EVM Smart Contracts](https://wiki.iota.org/isc/introduction/). ## Decentralized Identity + + IOTA also offers a Decentralized Identity framework which can be utilized to provide and use digital decentralized identities for People, Organizations, Things and Objects. -Go to the [IOTA Identity Framework](iota-identity/welcome.mdx) section. +Go to the [IOTA Identity Framework](https://wiki.iota.org/identity.rs/welcome/) section. diff --git a/docs/content/developer/exchange-integration/exchange-integration.mdx b/docs/content/developer/exchange-integration/exchange-integration.mdx index 720136c343d..25c2a2363a2 100644 --- a/docs/content/developer/exchange-integration/exchange-integration.mdx +++ b/docs/content/developer/exchange-integration/exchange-integration.mdx @@ -3,7 +3,6 @@ title: IOTA Exchange Integration Guide description: Learn the primary tasks necessary to integrate IOTA, the token native to the IOTA network, into a cryptocurrency exchange. --- -import AlphaNet from "../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/exchange-intergration/exchange-integration.json'; @@ -99,10 +98,9 @@ You can track balance changes by calling `iotax_getBalance` at predefined interv The following bash example demonstrates how to use `iotax_getBalance` for address `0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3`. If you use a network other than Devnet, replace the value for `rpc` with the URL to the appropriate Full node. - ```bash -rpc="https://fullnode.devnet.iota.org:443" +rpc="https://api.devnet.iota.cafe:443" address="0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3" data="{\"jsonrpc\": \"2.0\", \"method\": \"iotax_getBalance\", \"id\": 1, \"params\": [\"$address\"]}" curl -X POST -H 'Content-type: application/json' --data-raw "$data" $rpc @@ -123,8 +121,6 @@ The response is a JSON object that includes the `totalBalance` for the address: ### Using Rust - - The following example demonstrates using `iotax_getBalance` in Rust: ```rust use std::str::FromStr; @@ -134,7 +130,7 @@ use iota_sdk::IotaClientBuilder; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let iota = IotaClientBuilder::default().build( - "https://fullnode.devnet.iota.org:443", + "https://api.devnet.iota.cafe:443", ).await.unwrap(); let address = IotaAddress::from_str("0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3")?; let objects = iota.coin_read_api().get_balance(address, None).await?; diff --git a/docs/content/developer/getting-started/client-tssdk.mdx b/docs/content/developer/getting-started/client-tssdk.mdx index dd472b3e3b6..b9547823e86 100644 --- a/docs/content/developer/getting-started/client-tssdk.mdx +++ b/docs/content/developer/getting-started/client-tssdk.mdx @@ -4,7 +4,6 @@ tags: [ typescript, sdk, getting-started, install] --- import InfoPnpmRequired from "../../_snippets/info-pnpm-required.mdx"; -import AlphaNet from "../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/getting-started/client-tssdk.json'; @@ -97,7 +96,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render( , ); ``` - ### `IotaClientProvider` @@ -113,7 +111,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); const networks = { devnet: { url: getFullnodeUrl('devnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; ReactDOM.createRoot(document.getElementById('root')!).render( @@ -142,7 +140,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); const networks = { devnet: { url: getFullnodeUrl('devnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/docs/content/developer/getting-started/connect.mdx b/docs/content/developer/getting-started/connect.mdx index 28c2f10c238..1034f0bb658 100644 --- a/docs/content/developer/getting-started/connect.mdx +++ b/docs/content/developer/getting-started/connect.mdx @@ -4,7 +4,7 @@ tags: [ explanation, mainnet, testnet, devnet, cli, setup, install, getting-star --- import DataWipe from "../../_snippets/data-wipe.mdx"; -import AlphaNet from "../../_snippets/alphanet.mdx"; +import MainnetRunsStardust from '../../_snippets/mainnet-runs-stardust.mdx'; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/getting-started/connect.json'; @@ -26,9 +26,9 @@ IOTA provides the [IOTA command line interface (CLI)](/references/cli/client.mdx You can use the [CLI](iota-environment.mdx#iota-cli) or [SDKs](iota-environment.mdx#iota-sdks) to send transactions and read requests from using the [JSON-RPC](../../references/iota-api/json-rpc-format.mdx) and the following -endpoint: `https://fullnode..iota.io:443`. +endpoint: `https://api..iota.cafe:443`. - + ## Environment Set Up @@ -80,8 +80,6 @@ IOTA Full node server URL (Defaults to IOTA Testnet if not specified) : If you want to connect to the [Testnet](#testnet), you can simply press `Enter`. - - If not, specify your desired node URL. If you choose to add a custom URL, you should also add an alias for ease of access: @@ -125,11 +123,9 @@ iota client envs The command outputs the available environment aliases, with `(active)` denoting the currently active network. ```sh localnet => http://0.0.0.0:9000 (active) -alphanet => https://api.iota-rebased-alphanet.iota.cafe:443 +testnet => https://https://api.testnet.iota.cafe:443 ``` - - You can add a new environment using the `iota client new` command. You should specify and `alias` and `rpc` URL, for example: @@ -151,12 +147,6 @@ iota client switch --env ## IOTA Networks -### Alphanet - -A temporary network while still in early development, used before public launch of IOTA Rebased. Will be replaced by the Testnet afterwards. - - - ### Devnet The latest stable release. As such, it has the latest features that were tested in the `development` branch of the @@ -171,7 +161,7 @@ test your software in real-world conditions. :::info Testnet node validators -The IOTA Alphanet, Testnet and Devnet networks consist of four validator nodes operated by the IOTA Foundation. +The IOTA Testnet and Devnet networks consist of four validator nodes operated by the IOTA Foundation. ::: diff --git a/docs/content/developer/getting-started/create-a-module.mdx b/docs/content/developer/getting-started/create-a-module.mdx index a715d63903e..c4db8a8c43b 100644 --- a/docs/content/developer/getting-started/create-a-module.mdx +++ b/docs/content/developer/getting-started/create-a-module.mdx @@ -74,11 +74,11 @@ Keep in mind that if you [upgrade your package](../iota-101/move-overview/packag ### Entry Functions -Add the `entry` modifier to functions you want to call from a [programmable transaction block](../iota-101/transactions/ptb/prog-txn-blocks.mdx). All parameters passed to the function must be inputs to the transaction block, not results from other transactions in the block, nor can they be modified by previous transactions in the block. These functions can only return types with the `drop` ability. +Add the `entry` modifier to functions you want to call from a [programmable transaction block](../iota-101/transactions/ptb/programmable-transaction-blocks). All parameters passed to the function must be inputs to the transaction block, not results from other transactions in the block, nor can they be modified by previous transactions in the block. These functions can only return types with the `drop` ability. ### Public Functions -`public` functions can be called from a [programmable transaction block](../iota-101/transactions/ptb/prog-txn-blocks.mdx) or another module. +`public` functions can be called from a [programmable transaction block](../iota-101/transactions/ptb/programmable-transaction-blocks) or another module. #### Accessor Functions diff --git a/docs/content/developer/getting-started/get-coins.mdx b/docs/content/developer/getting-started/get-coins.mdx index 2e1f82334e5..0cc7704f423 100644 --- a/docs/content/developer/getting-started/get-coins.mdx +++ b/docs/content/developer/getting-started/get-coins.mdx @@ -2,7 +2,6 @@ description: Get test IOTA tokens using the IOTA CLI, SDK, or cURL. tags: [how-to, cli, typescript, sdk, faucet] --- -import AlphaNet from "../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/getting-started/get-coins.json'; @@ -26,7 +25,7 @@ You can request test tokens within IOTA Wallet. You can use the following cURL command to request tokens directly from the faucet server: ``` -curl --location --request POST 'https://faucet.iota-rebased-alphanet.iota.cafe/gas' \ +curl --location --request POST 'https://faucet.testnet.iota.cafe/gas' \ --header 'Content-Type: application/json' \ --data-raw '{ "FixedAmountRequest": { @@ -35,11 +34,9 @@ curl --location --request POST 'https://faucet.iota-rebased-alphanet.iota.cafe/g }' ``` - - :::tip Test tokens on a local network -If you're working with a local network, replace `'https://faucet.iota-rebased-alphanet.iota.cafe/gas'` with the appropriate value based on which package runs your network: +If you're working with a local network, replace `'https://faucet.devnet.iota.cafe/gas'` with the appropriate value based on which package runs your network: - `iota-faucet`: `http://127.0.0.1:5003/gas` - `iota start`: `http://127.0.0.1:9123/gas` diff --git a/docs/content/developer/getting-started/getting-started.mdx b/docs/content/developer/getting-started/getting-started.mdx index d1beaeceee3..f7eef5dbd7c 100644 --- a/docs/content/developer/getting-started/getting-started.mdx +++ b/docs/content/developer/getting-started/getting-started.mdx @@ -10,22 +10,17 @@ tags: # Getting Started IOTA Rebased introduces layer 1 Move smart contracts to the IOTA ecosystem. This valuable addition enhances IOTA by -offering programmability on layer1, complementing [IOTA EVM](../iota-evm/introduction.mdx) on layer 2. +offering programmability on layer1, complementing [IOTA EVM](../../about-iota/about-iota.mdx#iota-evm-and-shimmer-evm) on layer 2. The guides in this section will guide you as you start your IOTA Rebased journey. We recommend that you start by [setting up your development environment](iota-environment.mdx), and then move on to [install IOTA](install-iota.mdx). +## GraphQL queries -[//]: # (## GraphQL queries) +Use the GraphQL service for IOTA RPC to interact with on-chain data. -[//]: # () - -[//]: # (Use the GraphQL service for IOTA RPC to interact with on-chain data. ) - -[//]: # () - -[//]: # (Go to [Query IOTA RPC with GraphQL](getting-started/graphql-rpc.mdx).) +Go to [Query IOTA RPC with GraphQL](graphql-rpc.mdx). ## What's Next? diff --git a/docs/content/developer/getting-started/_graphql-rpc.mdx b/docs/content/developer/getting-started/graphql-rpc.mdx similarity index 96% rename from docs/content/developer/getting-started/_graphql-rpc.mdx rename to docs/content/developer/getting-started/graphql-rpc.mdx index 98b02f44ae1..08d3b45c726 100644 --- a/docs/content/developer/getting-started/_graphql-rpc.mdx +++ b/docs/content/developer/getting-started/graphql-rpc.mdx @@ -5,16 +5,15 @@ description: Intoductory guide to making queries of the IOTA RPC using the Graph The quickest way to access the GraphQL service for IOTA RPC is through the online IDE that provides a complete toolbox for fetching data and executing transactions on the network. The online IDE provides features such as auto-completion (use Ctrl+Space or just start typing), built-in documentation (Book icon, top-left), multi-tabs, and more. -The online IDE is available for [Mainnet](https://iota-mainnet.iota.org/graphql) and [Testnet](https://iota-testnet.iota.org/graphql). This guide contains various queries that you can try directly in the IDE. +The online IDE is available for [Devnet](https://graphql.devnet.iota.cafe/l) and [Testnet](https://graphql.testnet.iota.cafel). This guide contains various queries that you can try directly in the IDE. :::info -- Any existing addresses/object IDs in these examples refer to `mainnet` data only. -- Both [mainnet](https://iota-mainnet.iota.org/graphql) and [testnet](https://iota-testnet.iota.org/graphql) services are rate-limited to keep network throughput optimized. +- Any existing addresses/object IDs in these examples refer to `testnet` data only. +- Both [devnet](https://graphql.devnet.iota.cafe/l) and [testnet](https://graphql.testnet.iota.cafel) services are rate-limited to keep network throughput optimized. ::: For more details about some concepts used in the examples below, please see the [GraphQL concepts](../graphql-rpc.mdx) page, and consult the [reference](../../references/iota-graphql.mdx) for full documentation on the supported schema. - ## Discovering the schema GraphQL introspection exposes the schema supported by the RPC service. The IDE's "Docs" pane (Book icon, top-left) and Search dialog (Cmd+K on macOS or Ctrl+K on Windows and Linux) offer a way to browse introspection output interactively. diff --git a/docs/content/developer/getting-started/publish.mdx b/docs/content/developer/getting-started/publish.mdx index 50506de6f55..677306197df 100644 --- a/docs/content/developer/getting-started/publish.mdx +++ b/docs/content/developer/getting-started/publish.mdx @@ -90,7 +90,7 @@ iota client objects ### Accessing Your Package -After successfully publishing the package, you can use the `iota client call` command to execute individual functions or create sophisticated [programmable transaction blocks](../iota-101/transactions/ptb/prog-txn-blocks.mdx) that group multiple commands into a single, cost-effective transaction with the `iota client ptb` command. +After successfully publishing the package, you can use the `iota client call` command to execute individual functions or create sophisticated [programmable transaction blocks](../iota-101/transactions/ptb/programmable-transaction-blocks) that group multiple commands into a single, cost-effective transaction with the `iota client ptb` command. ```mermaid flowchart TB diff --git a/docs/content/developer/_graphql-rpc.mdx b/docs/content/developer/graphql-rpc.mdx similarity index 97% rename from docs/content/developer/_graphql-rpc.mdx rename to docs/content/developer/graphql-rpc.mdx index 9fb700c8685..e3a30aba770 100644 --- a/docs/content/developer/_graphql-rpc.mdx +++ b/docs/content/developer/graphql-rpc.mdx @@ -24,7 +24,7 @@ The service accepts the following optional headers: By default, each request returns the service's version in the response header: `x-iota-rpc-version`. ```bash -curl -i -X POST https://explorer.iota.org/mainnet/graphql \ +curl -i -X POST https://explorer.iota.org/testnet/graphql \ --header 'x-iota-rpc-show-usage: true' \ --header 'Content-Type: application/json' \ --data '{ @@ -247,7 +247,7 @@ It is an error to apply both a `first` and a `last` limit. ### Examples -To see these principles put into practice, consult the examples for [paginating forwards](getting-started/graphql-rpc.mdx#paginating-checkpoints-forward-five-at-a-time-page-forward) and [paginating backwards](getting-started/graphql-rpc.mdx#paginating-checkpoints-backwards-five-at-a-time-page-back) in the getting started guide. +To see these principles put into practice, consult the examples for [paginating forwards](getting-started/graphql-rpc.mdx#paginating-checkpoints-forward-five-at-a-time) and [paginating backwards](getting-started/graphql-rpc.mdx#paginating-checkpoints-backwards-five-at-a-time) in the getting started guide. ## Limits diff --git a/docs/content/developer/guides.mdx b/docs/content/developer/guides.mdx index bac5ee7446f..e4b9f24759c 100644 --- a/docs/content/developer/guides.mdx +++ b/docs/content/developer/guides.mdx @@ -36,7 +36,7 @@ With IOTA installed, you're ready to start developing. Learn the basics of IOTA and how they might differ from other blockchains. - + Transactions on IOTA are more powerful than other blockchains. Learn why and how to use them. diff --git a/docs/content/developer/iota-101.mdx b/docs/content/developer/iota-101.mdx index 028527d99e7..00ae371b7da 100644 --- a/docs/content/developer/iota-101.mdx +++ b/docs/content/developer/iota-101.mdx @@ -16,7 +16,7 @@ Everything on the IOTA blockchain is an object. These topics use code examples t You can create programmable transaction blocks (PTBs) on IOTA to perform multiple commands in a single transaction. The Working with PTBs topics demonstrate how to build efficient PTBs using the IOTA TypeScript SDK. -Go to [Working with PTBs](iota-101/transactions/ptb/working-with-ptbs.mdx). +Go to [Working with PTBs](iota-101/transactions/ptb/programmable-transaction-blocks-overview). ## Using Events diff --git a/docs/content/developer/iota-101/move-overview/generics.mdx b/docs/content/developer/iota-101/move-overview/generics.mdx new file mode 100644 index 00000000000..54c0c26c46f --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/generics.mdx @@ -0,0 +1,116 @@ +# Generics + +Generics are a way to define a type or function that can work with any type. This is useful when you +want to write a function which can be used with different types, or when you want to define a type +that can hold any other type. Generics are the foundation of many advanced features in Move, such as +collections, abstract implementations, and more. + +## In the Standard Library + +An example of a generic type is the [vector](../../../references/framework/move-stdlib/vector.mdx) type, which is a container type that +can hold any other type. Another example of a generic type in the standard library is the +[Option](../../../references/framework/move-stdlib/option.mdx) type, which is used to represent a value that may or may not be present. + +## Generic Syntax + +To define a generic type or function, a type signature needs to have a list of generic parameters +enclosed in angle brackets (`<` and `>`). The generic parameters are separated by commas. + +```move file=/docs/examples/move/move-overview/generics.move#L8-L16 +``` + +In the example above, `Container` is a generic type with a single type parameter `T`, the `value` +field of the container stores the `T`. The `new` function is a generic function with a single type +parameter `T`, and it returns a `Container` with the given value. Generic types must be initialed +with a concrete type, and generic functions must be called with a concrete type. + +```move file=/docs/examples/move/move-overview/generics.move#L20-L31 +``` + +In the test function `test_generic` we demonstrate three equivalent ways to create a new `Container` +with a `u8` value. Because numeric types need to be inferred, we specify the type of the number +literal. + +## Multiple Type Parameters + +You can define a type or function with multiple type parameters. The type parameters are then +separated by commas. + +```move file=/docs/examples/move/move-overview/generics.move#L35-L44 +``` + +In the example above, `Pair` is a generic type with two type parameters `T` and `U`, and the +`new_pair` function is a generic function with two type parameters `T` and `U`. The function returns +a `Pair` with the given values. The order of the type parameters is important, and it should match +the order of the type parameters in the type signature. + +```move file=/docs/examples/move/move-overview/generics.move#L48-L63 +``` + +If we added another instance where we swapped type parameters in the `new_pair` function, and tried +to compare two types, we'd see that the type signatures are different, and cannot be compared. + +```move file=/docs/examples/move/move-overview/generics.move#L67-L80 +``` + +Types for variables `pair1` and `pair2` are different, and the comparison will not compile. + +## Why Generics? + +In the examples above we focused on instantiating generic types and calling generic functions to +create instances of these types. However, the real power of generics is the ability to define shared +behavior for the base, generic type, and then use it independently of the concrete types. This is +especially useful when working with collections, abstract implementations, and other advanced +features in Move. + +```move file=/docs/examples/move/move-overview/generics.move#L86-L92 +``` + +In the example above, `User` is a generic type with a single type parameter `T`, with shared fields +`name` and `age`, and the generic `metadata` field which can store any type. No matter what the +`metadata` is, all of the instances of `User` will have the same fields and methods. + +```move file=/docs/examples/move/move-overview/generics.move#L96-L104 +``` + +## Phantom Type Parameters + +In some cases, you may want to define a generic type with a type parameter that is not used in the +fields or methods of the type. This is called a _phantom type parameter_. Phantom type parameters +are useful when you want to define a type that can hold any other type, but you want to enforce some +constraints on the type parameter. + +```move file=/docs/examples/move/move-overview/generics.move#L108-L111 +``` + +The `Coin` type here does not contain any fields or methods that use the type parameter `T`. It is +used to differentiate between different types of coins, and to enforce some constraints on the type +parameter `T`. + +```move file=/docs/examples/move/move-overview/generics.move#L115-L126 +``` + +In the example above, we demonstrate how to create two different instances of `Coin` with different +phantom type parameters `USD` and `EUR`. The type parameter `T` is not used in the fields or methods +of the `Coin` type, but it is used to differentiate between different types of coins. It will make +sure that the `USD` and `EUR` coins are not mixed up. + +## Constraints on Type Parameters + +Type parameters can be constrained to have certain abilities. This is useful when you need the inner +type to allow certain behavior, such as _copy_ or _drop_. The syntax for constraining a type +parameter is `T: + `. + +```move file=/docs/examples/move/move-overview/generics.move#L130-L138 +``` + +Move Compiler will enforce that the type parameter `T` has the specified abilities. If the type +parameter does not have the specified abilities, the code will not compile. + + +```move file=/docs/examples/move/move-overview/generics.move#L142-L152 +``` + +## Further Reading + +- [Generics](../../../references/move/generics.mdx) in the Move Reference. \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/ownership-scope.mdx b/docs/content/developer/iota-101/move-overview/ownership-scope.mdx new file mode 100644 index 00000000000..c2ee2658722 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/ownership-scope.mdx @@ -0,0 +1,143 @@ +--- +title: Ownership and Scope +description: Learn about variable ownership and scope in Move, how it works, and how to use it in Move. +--- + +# Ownership and Scope + +:::info +This section covers the concept of variable ownership and scope in Move, not to be confused with [object ownership](../objects/object-ownership/object-ownership.mdx) in the IOTA framework. +::: + +Move's ownership and scope management was inspired by [Rust](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html): Every variable in Move has a scope and an owner. The scope is the range of code where the variable +is valid, and the owner is the scope that this variable belongs to. Once the owner’s scope ends, the +variable is dropped. This is a fundamental concept in Move, and it is important to understand how it +works. + + +## Ownership + +A variable defined in a function scope is owned by this scope. The runtime goes through the function +scope and executes every expression and statement. Once the function scope ends, the variables +defined in it are dropped or deallocated. + +```move +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function +} // a is dropped here + +public fun other() { + let b = 2; // b is owned by the `other` function +} // b is dropped here + +#[test] +fun test_owner() { + owner(); + other(); + // a & b is not valid here +} +``` + +In the example above, the variable `a` is owned by the `owner` function, and the variable `b` is +owned by the `other` function. When each of these functions are called, the variables are defined, +and when the function ends, the variables are discarded. + +## Returning a Value + +If we changed the `owner` function to return the variable `a`, then the ownership of `a` would be +transferred to the caller of the function. + +```move +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a defined here + a // scope ends, a is returned +} + +#[test] +fun test_owner() { + let a = owner(); + // a is valid here +} // a is dropped here +``` + +## Passing by Value + +Additionally, if we passed the variable `a` to another function, the ownership of `a` would be +transferred to this function. When performing this operation, we _move_ the value from one scope to +another. This is also called _move semantics_. + +```move +module book::ownership; + +public fun owner(): u8 { + let a = 10; + a +} // a is returned + +public fun take_ownership(v: u8) { + // v is owned by `take_ownership` +} // v is dropped here + +#[test] +fun test_owner() { + let a = owner(); + take_ownership(a); + // a is not valid here +} +``` + +## Scopes with Blocks + +Each function has a main scope, and it can also have sub-scopes via the use of blocks. A block is a +sequence of statements and expressions, and it has its own scope. Variables defined in a block are +owned by this block, and when the block ends, the variables are dropped. + +```move +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function's scope + { + let b = 2; // b is owned by the block + { + let c = 3; // c is owned by the block + }; // c is dropped here + }; // b is dropped here + // a = b; // error: b is not valid here + // a = c; // error: c is not valid here +} // a is dropped here +``` + +However, shall we use the return value of a block, the ownership of the variable is transferred to +the caller of the block. + +```move +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a is owned by the `owner` function's scope + let b = { + let c = 2; // c is owned by the block + c // c is returned + }; // c is dropped here + a + b // both a and b are valid here +} +``` + +## Copyable Types + +Some types in Move are _copyable_, which means that they can be copied without transferring the +ownership. This is useful for types that are small and cheap to copy, such as integers and booleans. +Move compiler will automatically copy these types when they are passed to a function or returned +from a function, or when they're _moved_ to a scope and then accessed in their original scope. + + +## Next Steps + +There are scenarios where we want to pass a variable to a function without transferring the ownership. +In this case, we can use references. To complete the understanding of ownership and scope, we recommend reading about: +- [References](./references.mdx) \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/references.mdx b/docs/content/developer/iota-101/move-overview/references.mdx new file mode 100644 index 00000000000..ce59e61612a --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/references.mdx @@ -0,0 +1,77 @@ +--- +title: References +description: References in Move allow passing a value to a function without giving up the ownership. This section covers references, how they work, and how to use them in Move. +--- + +# References + +In the [Ownership and Scope](./ownership-scope.mdx) section, we explained that when a value is +passed to a function, the value's ownership is transferred to the function. This means that the function becomes +the owner of the value, and the original scope (owner) can no longer use it. This is an important +concept in Move, as it ensures that the value is not used in multiple places at the same time. +However, there are use cases when we want to pass a value to a function but retain the ownership. +This is where references come into play. + +To illustrate this, let's consider a simple example - an application for a metro (subway) pass card that can be: + +1. Purchased at the kiosk for a fixed price. +2. Shown to inspectors to prove that the passenger has a valid pass. +3. Used at the turnstile to enter the metro, and spend a ride. +4. Recycled once it's empty. + +## Layout +{/*TODO: Add refs for constants and error constants*/} +The initial layout of the metro pass application is simple. We define the `Card` type and the `USES` +constant that represents the number of rides for a single card. We also add an error constant for the case when the card is empty. + +```move file=/docs/examples/move/move-overview/references.move#L7-L19 +``` + +## Reference + +References are a way to _show_ a value to a function without giving up the ownership. In our case, +when we show the Card to the inspector, we don't want to give up the ownership of it, and we don't +allow them to spend the rides. We just want to allow _reading_ the value of the Card and prove its +ownership. + +To do so, in the function signature, we use the `&` symbol to indicate that we are passing a +reference to the value, not the value itself. + +```move file=/docs/examples/move/move-overview/references.move#L21-L24 +``` + +Now the function can't take the ownership of the card, and it can't spend the rides. But it can read +its value. It's worth noting, that a signature like this makes it impossible to call the function without +a Card at all. This is an important property which allows the +[Capability Pattern](./patterns/capabilities.mdx). + +## Mutable Reference + +In some cases, we want to allow the function to change the value of the Card. For example, when we +use the Card at the turnstile, we want to spend a ride. To implement it, we use the `&mut` keyword +in the function signature. + +```move file=/docs/examples/move/move-overview/references.move#L26-L30 +``` + +As you can see in the function body, the `&mut` reference allows mutating the value, and the +function can spend the rides. + +## Passing by Value + +Lastly, let's give an illustration of what happens when we pass the value itself to the function. In +this case, the function takes the ownership of the value, and the original scope can no longer use +it. The owner of the Card can recycle it, and, hence, lose the ownership. + +```move file=/docs/examples/move/move-overview/references.move#L32-L36 +``` + +In the `recycle` function, the Card is _taken by value_ and can be unpacked and destroyed. The +original scope can't use it anymore. + +## Full Example + +To illustrate the full flow of the application, let's put all the pieces together including some tests. + +```move file=/docs/examples/move/move-overview/references.move +``` \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/abilities-intro.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/abilities-intro.mdx new file mode 100644 index 00000000000..1be9c510045 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/abilities-intro.mdx @@ -0,0 +1,65 @@ +--- +title: Abilities +description: Abilities in Move allow customizing type behaviors. This section covers the introduction to abilities, how they work, and how to use them in Move. +--- + +# Abilities: Introduction + +Move has a unique type system which allows customizing _type abilities_. +In the [struct section](./struct.mdx), we introduced the `struct` definition and how to use it. +However, the instances of the `Artist` and `Record` structs had to be unpacked for the code to +compile. This is default behavior of a struct without _abilities_. + +::: info +Throughout the documentation, you will see struct definitions with abilities. This section will cover abilities in detail, how they work, and how to use them +in Move. +::: + +## What are Abilities? + +Abilities are a way to allow certain behaviors for a type. They are a part of the struct declaration +and define which behaviours are allowed for the instances of the struct. + +## Abilities syntax + +Abilities are set in the struct definition using the `has` keyword followed by a list of abilities. +The abilities are separated by commas. Move supports 4 abilities: `copy`, `drop`, `key`, and +`store`, each of them is used to define a specific behaviour for the struct instances. + +```move +/// This struct has the `copy` and `drop` abilities. +struct VeryAble has copy, drop { + // field: Type1, + // field2: Type2, + // ... +} +``` + +## Overview + +A quick overview of the abilities: + +:::info +All of the built-in types, except references, have `copy`, `drop` and `store` abilities. +References have `copy` and `drop`. +::: + +- `copy` - allows the struct to be _copied_. Explained in the [Ability: Copy](./copy.mdx) + section. +- `drop` - allows the struct to be _dropped_ or _discarded_. Explained in the + [Ability: Drop](./drop.mdx) section. +- `key` - allows the struct to be used as a _key_ in a storage. Explained in the + [Ability: Key](./key.mdx) section. +- `store` - allows the struct to be _stored_ in structs with the _key_ ability. Explained in the + [Ability: Store](./store.mdx) section. + +While it is important to mention them here, we will go in detail about each ability in the following +sections and give a proper context on how to use them. + +## No Abilities + +A struct without abilities cannot be discarded, or copied, or stored. We call such a +struct a _Hot Potato_. It is a joke, but it is also a good way to remember that a struct without +abilities is like a hot potato - it can only be passed around and requires special handling. Hot +Potato is one of the most powerful patterns in Move, we go in detail about it in the +[Hot Potato](../patterns/hot-potato.mdx) chapter. diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/copy.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/copy.mdx new file mode 100644 index 00000000000..0dec2ad259a --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/copy.mdx @@ -0,0 +1,56 @@ +--- +title : Copy Ability +description: In Move, the copy ability on a type indicates that the instance or the value of the type can be copied. This section covers the copy ability, how it works, and how to use it in Move. +--- + + +# Copy Ability + +In Move, the _copy_ ability on a type indicates that the instance or the value of the type can be +copied. While this behavior may feel very natural when working with numbers or other simple types, +it is not the default for custom types in Move. This is because Move is designed to express digital +assets and resources, and inability to copy is a key element of the resource model. + +However, Move type system allows you to define custom types with the _copy_ ability. + +```move file=/docs/examples/move/move-overview/copy-ability.move#L8 +``` + +In the example above, we define a custom type `Copyable` with the _copy_ ability. This means that +instances of `Copyable` can be copied, both implicitly and explicitly. + +```move file=/docs/examples/move/move-overview/copy-ability.move#L13-L20 +``` + +In the example above, `a` is copied to `b` implicitly, and then explicitly copied to `c` using the +dereference operator. If `Copyable` did not have the _copy_ ability, the code would not compile, and +the Move compiler would raise an error. + +## Copy and Drop + +The `copy` ability is closely related to [`drop` ability](./drop.mdx). If a type has the +_copy_ ability, very likely that it should have `drop` too. This is because the _drop_ ability is +required to clean up the resources when the instance is no longer needed. If a type has only _copy_, +then managing its instances gets more complicated, as the values cannot be ignored. + +```move file=/docs/examples/move/move-overview/copy-ability.move#L25 +``` + +All of the primitive types in Move behave as if they have the _copy_ and _drop_ abilities. This +means that they can be copied and dropped, and the Move compiler will handle the memory management +for them. + +## Types With the `copy` Ability + +All native types in Move have the `copy` ability. This includes: + +- bool +- unsigned integers +- [vector](../../../../references/framework/move-stdlib/vector.mdx) +- [address](../../../../references/framework/move-stdlib/address.mdx) + +All of the types defined in the standard library have the `copy` ability as well. This includes: + +- [Option](../../../../references/framework/move-stdlib/option.mdx) +- [String](../../../../references/framework/move-stdlib/string.mdx) +- [TypeName](../../../../references/framework/move-stdlib/type_name.mdx) \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/drop.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/drop.mdx new file mode 100644 index 00000000000..072b2b0c4f8 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/drop.mdx @@ -0,0 +1,42 @@ +--- +title: Drop Ability +description: In Move, the drop ability on a type indicates that the instance of the type can be ignored or discarded. This section covers the drop ability, how it works, and how to use it in Move. +--- + + +# Drop Ability + +The `drop` ability - the simplest of abilities - allows the instance of a struct to be _ignored_ or +_discarded_. In many programming languages this behavior is considered default. However, in Move, a +struct without the `drop` ability is not allowed to be ignored. This is a safety feature of the Move +language, which ensures that all assets are properly handled. An attempt to ignore a struct without +the `drop` ability will result in a compilation error. + +```move file=/docs/examples/move/move-overview/drop-ability.move#L5-L25 +``` + + +The `drop` ability is often used on custom collection types to eliminate the need for special +handling of the collection when it is no longer needed. For example, a `vector` type has the `drop` +ability, which allows the vector to be ignored when it is no longer needed. However, the biggest +feature of Move's type system is the ability to not have `drop`. This ensures that the assets are +properly handled and not ignored. + +A struct with a single `drop` ability is called a _Witness_. We explain the concept of a _Witness_ +in the [Witness and Abstract Implementation](../patterns/witness.mdx) +section. + +## Types With the `drop` Ability + +All native types in Move have the `drop` ability. This includes: + +- bool +- unsigned integers +- [vector](../../../../references/framework/move-stdlib/vector.mdx) +- [address](../../../../references/framework/move-stdlib/address.mdx) + +All of the types defined in the standard library have the `drop` ability as well. This includes: + +- [Option](../../../../references/framework/move-stdlib/option.mdx) +- [String](../../../../references/framework/move-stdlib/string.mdx) +- [TypeName](../../../../references/framework/move-stdlib/type_name.mdx) \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/key.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/key.mdx new file mode 100644 index 00000000000..cc6060a0491 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/key.mdx @@ -0,0 +1,59 @@ +--- +title: Key Ability +description: In Move, the key ability on a type indicates that the instance of the type can be stored in the storage. This section covers the key ability, how it works, and how to use it in Move. +--- + + +# Key Ability + +We already covered two out of four abilities - +[Drop](./drop.mdx) and [Copy](./copy.mdx). They affect the behaviour of the value in a +scope and are not directly related to storage. It is time to cover the `key` ability, which allows +the struct to be stored. + +Historically, the `key` ability was created to mark the type as a _key in the storage_. A type with +the `key` ability could be stored at top-level in the storage, and could be _directly owned_ by an +account or address. With the introduction of the [Object Model](../../objects/object-model.mdx), the `key` ability +naturally became the defining ability for the object. + + +## Object Definition + +A struct with the `key` ability is considered an object and can be used in the storage functions. +The Sui Verifier will require the first field of the struct to be named `id` and have the type +`UID`. + +```move +public struct Object has key { + id: UID, // required + name: String, +} + +/// Creates a new Object with a Unique ID +public fun new(name: String, ctx: &mut TxContext): Object { + Object { + id: object::new(ctx), // creates a new UID + name, + } +} +``` + +A struct with the `key` ability is still a struct, and can have any number of fields and associated +functions. There is no special handling or syntax for packing, accessing or unpacking the struct. + +However, because the first field of an object struct must be of type `UID` - a non-copyable and +non-droppable type (we will get to it very soon!), the struct transitively cannot have `drop` and +`copy` abilities. Thus, the object is non-discardable by design. + + +## Types With the `key` Ability + +Due to the `UID` requirement for types with `key`, none of the native types in Move can have the +`key` ability, nor can any of the [Standard Library](../../../../references/framework/move-stdlib/_category_.json) types. +The `key` ability is only present in the [IOTA Framework](../../../../references/framework/iota-framework/_category_.json) and +custom types. + +## Next Steps + +Key ability defines the object in Move, and objects are intended to be _stored_. In the next section +we present the `iota::transfer` module, which provides native storage functions for objects. \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/store.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/store.mdx new file mode 100644 index 00000000000..aedd202e8a5 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/store.mdx @@ -0,0 +1,55 @@ +--- +title: Store Ability +description: In Move, the store ability on a type indicates that the instance of the type can be stored in objects. This section covers the store ability, how it works, and how to use it in Move. +--- + +# Store Ability + +## Definition + +The `store` is a special ability that allows a type to be _stored_ in objects. This ability is +required for the type to be used as a field in a struct that has the `key` ability. Another way to +put it is that the `store` ability allows the value to be _wrapped_ in an object. + +:::info +The `store` ability also relaxes restrictions on transfer operations. We talk about it more in the +[Custom Transfer Rules](../../objects/transfers/custom-rules.mdx) section. +::: + +## Example + +In previous sections we already used types with the `key` ability: all objects must have a `UID` +field, which we used in examples; we also used the `Storable` type as a part of the `Config` struct. +The `Config` type also has the `store` ability. + +```move +/// This type has the `store` ability. +public struct Storable has store {} + +/// Config contains a `Storable` field which must have the `store` ability. +public struct Config has key, store { + id: UID, + stores: Storable, +} + +/// MegaConfig contains a `Config` field which has the `store` ability. +public struct MegaConfig has key { + id: UID, + config: Config, // there it is! +} +``` + +## Types With the `store` Ability + +All native types (except for references) in Move have the `store` ability. This includes: + +- bool +- unsigned integers +- [vector](../../../../references/framework/move-stdlib/vector.mdx) +- [address](../../../../references/framework/move-stdlib/address.mdx) + +All of the types defined in the standard library have the `store` ability as well. This includes: + +- [Option](../../../../references/framework/move-stdlib/option.mdx) +- [String](../../../../references/framework/move-stdlib/string.mdx) +- [TypeName](../../../../references/framework/move-stdlib/type_name.mdx) \ No newline at end of file diff --git a/docs/content/developer/iota-101/move-overview/structs-and-abilities/struct.mdx b/docs/content/developer/iota-101/move-overview/structs-and-abilities/struct.mdx new file mode 100644 index 00000000000..1797bceafe7 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/structs-and-abilities/struct.mdx @@ -0,0 +1,79 @@ +--- +title: Struct +description: Custom types in Move can be defined using the struct keyword. This section covers the struct definition, how to create and use an instance, and how to unpack a struct. +--- + + +# Custom Types with Struct + +Move's type system offers the ability to define custom types. User defined types can be custom +tailored to the specific needs of the application. Not just on the data level, but also in its +behavior. In this section we introduce the struct definition and how to use it. + +## Struct + +To define a custom type, you can use the `struct` keyword followed by the name of the type. After +the name, you can define the fields of the struct. Each field is defined with the +`field_name: field_type` syntax. Field definitions must be separated by commas. The fields can be of +any type, including other structs. + +:::info +Move does not support recursive structs, meaning a struct cannot contain itself as a field. +::: + +```move file=/docs/examples/move/move-overview/struct.move#L10-L28 +``` + + +In the example above, we define a `Record` struct with five fields. The `title` field is of type +`String`, the `artist` field is of type `Artist`, the `year` field is of type `u16`, the `is_debut` +field is of type `bool`, and the `edition` field is of type `Option`. The `edition` field is of +type `Option` to represent that the edition is optional. + +Structs are private by default, meaning they cannot be imported and used outside of the module they +are defined in. Their fields are also private and can't be accessed from outside the module. See +[visibility](../visibility.mdx) for more information on different visibility modifiers. + +:::info +Fields of a struct are private and can only be accessed by the module defining the struct. Reading +and writing the fields of a struct in other modules is only possible if the module defining the +struct provides public functions to access the fields. +::: + +## Create and Use an Instance + +We described how struct _definition_ works. Now let's see how to initialize a struct and use it. A +struct can be initialized using the `struct_name { field1: value1, field2: value2, ... }` syntax. +The fields can be initialized in any order, and all of the fields must be set. + +```move file=/docs/examples/move/move-overview/struct.move#L34-L36 +``` + +In the example above, we create an instance of the `Artist` struct and set the `name` field to a +string "The Beatles". + +To access the fields of a struct, you can use the `.` operator followed by the field name. + +```move file=/docs/examples/move/move-overview/struct.move#L40-L50 +``` + +Only module defining the struct can access its fields (both mutably and immutably). So the above +code should be in the same module as the `Artist` struct. + + +## Unpacking a Struct + +Structs are non-discardable by default, meaning that the initiated struct value must be used: either +stored or _unpacked_. Unpacking a struct means deconstructing it into its fields. This is done using +the `let` keyword followed by the struct name and the field names. + +```move file=/docs/examples/move/move-overview/struct.move#L54-L56 +``` + +In the example above we unpack the `Artist` struct and create a new variable `name` with the value +of the `name` field. Because the variable is not used, the compiler will raise a warning. To +suppress the warning, you can use the underscore `_` to indicate that the variable is intentionally +unused. + +```move file=/docs/examples/move/move-overview/struct.move#L64-L65 +``` diff --git a/docs/content/developer/iota-101/move-overview/visibility.mdx b/docs/content/developer/iota-101/move-overview/visibility.mdx new file mode 100644 index 00000000000..626edd0ff22 --- /dev/null +++ b/docs/content/developer/iota-101/move-overview/visibility.mdx @@ -0,0 +1,84 @@ +# Visibility Modifiers + +Every module member has a visibility. By default, all module members are _private_ - meaning they +are only accessible within the module they are defined in. However, you can add a visibility +modifier to make a module member _public_ - visible outside the module, or _public(package)_ - +visible in the modules within the same package, or [_entry_](./entry-functions.mdx) - can be called from a transaction but +can't be called from other modules. + +## Internal Visibility + +A function or a struct defined in a module which has no visibility modifier is _private_ to the +module. It can't be called from other modules. + +```move +module book::internal_visibility; + +// This function can be called from other functions in the same module +fun internal() { /* ... */ } + +// Same module -> can call internal() +fun call_internal() { + internal(); +} +``` + +```move +module book::try_calling_internal; + +use book::internal_visibility; + +// Different module -> can't call internal() +fun try_calling_internal() { + internal_visibility::internal(); +} +``` + +## Public Visibility + +A struct or a function can be made _public_ by adding the `public` keyword before the `fun` or +`struct` keyword. + +```move +module book::public_visibility; + +// This function can be called from other modules +public fun public() { /* ... */ } +``` + +A public function can be imported and called from other modules. The following code will compile: + +```move +module book::try_calling_public { + +use book::public_visibility; + +// Different module -> can call public() +fun try_calling_public() { + public_visibility::public(); +} +``` + +## Package Visibility + +Move 2024 introduces the _package visibility_ modifier. A function with _package visibility_ can be +called from any module within the same package. It can't be called from other packages. + +```move +module book::package_visibility; + +public(package) fun package_only() { /* ... */ } +``` + +A package function can be called from any module within the same package: + +```move +module book::try_calling_package; + +use book::package_visibility; + +// Same package `book` -> can call package_only() +fun try_calling_package() { + package_visibility::package_only(); +} +``` \ No newline at end of file diff --git a/docs/content/developer/iota-101/objects/uid-id.mdx b/docs/content/developer/iota-101/objects/uid-id.mdx new file mode 100644 index 00000000000..7340b19a43d --- /dev/null +++ b/docs/content/developer/iota-101/objects/uid-id.mdx @@ -0,0 +1,94 @@ +# UID and ID + +The `UID` type is defined in the `iota::object` module and is a wrapper around an `ID` which, in +turn, wraps the [`address` type](../../../references/framework/move-stdlib/address.mdx). The UIDs on IOTA are guaranteed to be unique, and can't be reused +after the object was deleted. + +```move +/// UID is a unique identifier of an object +public struct UID has store { + id: ID +} + +/// ID is a wrapper around an address +public struct ID has store, drop { + bytes: address +} +``` + + +## Fresh UID Generation + +- A UID is derived from the `tx_hash` and an `index` which is incremented for each new UID. +- The `derive_id` function is implemented in the `iota::tx_context` module, and that is why TxContext + is required for UID generation. +- IOTA Verifier will not allow using a UID that wasn't created in the same function. That prevents + UIDs from being pre-generated and reused after the object was unpacked. + +You can create a new UID with the [`object::new(ctx)` function](../../../references/framework/iota-framework/object.mdx#function-new). It takes a mutable reference to TxContext, +and returns a new UID. + +```move +let ctx = &mut tx_context::dummy(); +let uid = object::new(ctx); +``` + +On IOTA, `UID` acts as a representation of an object, and allows defining behaviors and features of +an object. One of the key-features - [Dynamic Fields](./dynamic-fields/dynamic-fields.mdx) - is possible because of the `UID` type +being explicit. Additionally, it allows the [Transfer To Object (TTO)](./transfers/transfer-to-object.mdx) which we will explain later +in this chapter. + +## UID Lifecycle + +The `UID` type is created with the [`object::new(ctx)` function](../../../references/framework/iota-framework/object.mdx#function-new), and it is destroyed with the +[`object::delete(uid)` function](../../../references/framework/iota-framework/object.mdx#function-delete). The `object::delete` consumes the UID _by value_, and it is +impossible to delete it unless the value was unpacked from an Object. + +```move +let ctx = &mut tx_context::dummy(); + +let char = Character { + id: object::new(ctx) +}; + +let Character { id } = char; +id.delete(); +``` + +## Keeping the UID + +The `UID` does not need to be deleted immediately after the object struct is unpacked. Sometimes it +may carry [Dynamic Fields](./dynamic-fields/dynamic-fields.mdx) or objects transferred to it via +[Transfer To Object](./transfers/transfer-to-object.mdx). In such cases, the UID may be kept and stored in a +separate object. + +## Proof of Deletion + +The ability to return the UID of an object may be utilized in pattern called _proof of deletion_. It +is a rarely used technique, but it may be useful in some cases, for example, the creator or an +application may incentivize the deletion of an object by exchanging the deleted IDs for some reward. + +In framework development this method could be used to bypass certain restrictions on +"taking" the object. If there's a container that enforces certain logic on transfers, like Kiosk +does, there could be a special scenario of skipping the checks by providing a proof of deletion. + +This is one of the open topics for exploration and research, and it may be used in various ways. + +## ID + +When talking about `UID` we should also mention the `ID` type. It is a wrapper around the [`address`](../../../references/framework/move-stdlib/address.mdx) +type, and is used to represent an address-pointer. Usually, `ID` is used to point at an object, +however, there's no restriction, and no guarantee that the `ID` points to an existing object. + +:::info +ID can be received as a transaction argument in a +[Transaction Block](../transactions/transactions.mdx). Alternatively, ID can be created from +an `address` value using `to_id()` function. +::: + + +## `fresh_object_address` + +TxContext provides the `fresh_object_address` function which can be utilized to create unique +addresses and `ID` - it may be useful in some application that assign unique identifiers to user +actions - for example, an order_id in a marketplace. \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/gas-smashing.mdx b/docs/content/developer/iota-101/transactions/gas-smashing.mdx deleted file mode 100644 index e3f1d4f9582..00000000000 --- a/docs/content/developer/iota-101/transactions/gas-smashing.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Gas Smashing -description: IOTA optimizes coin management by combining multiple coins into a single object to pay for gas fees. ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/gas-smashing.json'; - -Every transaction on IOTA has a gas fee associated with its execution that must be paid to successfully execute the transaction. Gas smashing enables you to pay for this gas fee using multiple coins instead of just one. This mechanism is especially helpful in scenarios where you might have a number of coins with smaller denominations, or if you simply want to minimize the number of IOTA coins under your account. Gas smashing is generally a useful tool for coin management, especially when coupled with the `GasCoin` programmable transaction block (PTB) argument. - -## Smashing gas - -Gas smashing happens automatically in a transaction if you provide multiple coins to pay for the gas fee. When IOTA executes a transaction, IOTA combines, or "smashes", all of the coins you provide to pay for the gas into a single coin. The smashing occurs regardless of coin amounts or the gas budget provided with the transaction (as long as it is within the minimum and maximum gas budgets). IOTA deducts the gas fee from the single coin regardless of the execution status of the transaction. In particular, this means that even if the transaction fails to execute for some reason (such as an execution error) the coins that you provided as gas coins remain smashed after the transaction's execution. - -Gas smashing is an efficient way to perform coin management, and to combine many smaller coins into one single coin that you can use to not only pay for gas fees, but also for other operations in the transaction that smashes the coins. In particular, you can use gas smashing to combine multiple coins to pay for the gas fee of the PTB, and then that same PTB can withdraw from the `GasCoin`. A special argument is available in PTBs that references the coin used to pay gas after IOTA deducts the gas fee from it, which you can use to transfer the remaining IOTA to another address. Because gas smashing happens automatically in a transaction if you provide multiple gas coins, you can combine multiple coins as part of other transactions to perform coin management in parallel with non-coin-management specific transactions. - -IOTA has a maximum of 256 coins that you can smash in a single PTB - the transaction is not processed if the number of gas coins exceeds this amount. Additionally, when you smash gas coins, IOTA deletes all but the first coin. Because of this, there is often a storage rebate associated with the deletion of these coins. As with other storage rebates, you can't use the resulting refund to pay for the gas fee of the transaction (and isn't credited to the coin until after the execution of the transaction), but it might result in a refund after the execution of the transaction. This refund, along with the remaining balance after the transaction's gas fee, resides in the first gas coin you provide in the transaction after execution. - -## Running out of gas with a refund - -Because coins are smashed regardless of the execution status, storage rebates can lead to seemingly odd cases where a transaction can both run out of gas and end up with a negative gas and storage fee (you get a refund from the transaction). As an example of how this might happen, take a transaction `T` that has a gas budget of `5000`, and you provide coins `C1`, `C2`, `C3`, `C4`, `C5` with values of `1000`, `2000`, `3000`, `4000`, and `5000` to pay for gas. If the storage rebate for a coin object is `2000`, and the gas fee for `T` execution is more than the provided gas budget of `5000` (so, `T` runs out of gas), this results in an execution status of `OutOfGas`. The `C1` coin has a balance of `1000` + `2000` + `3000` + `4000` + `5000` - `5000` + `2000` * `4` = `15000` - `5000` + `8000` = `18000` and `T` ends up with a negative gas and storage fee (a refund) of `3000`. - - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx b/docs/content/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx new file mode 100644 index 00000000000..70b171a6459 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx @@ -0,0 +1,294 @@ +--- +description: Learn how to create programmable transaction blocks using the IOTA TypeScript SDK. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.json'; + +# Build Programmable Transaction Blocks with TypeScript SDK + +This guide illustrates how to build a [programmable transaction block (PTB)](programmable-transaction-blocks.mdx) on IOTA +using the [TypeScript SDK](../../../../references/ts-sdk/typescript/install.mdx). + +This example starts by building a PTB to send IOTA tokens. +To construct transactions, import the `Transaction` class and create a new instance: + +```ts +import { Transaction } from "@iota/iota-sdk"; +const txb = new Transaction(); +``` + +With this instance, you can add transactions to the PTB: + +```ts +// Generate a new coin with a balance of 100, derived from the coins used for gas payment. +// You can specify any balance here. +const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]); + +// Transfer the newly created coin to a specific address. +txb.transferObjects([coin], txb.pure("0xSomeIOTAAddress")); +``` + +You can also attach multiple transaction commands of the same type to a PTB. +For instance, to process a list of transfers and send coins to each recipient: + +```ts +interface Transfer { + to: string; + amount: number; +} + +// Obtain a list of IOTA transfers to execute: +const transfers: Transfer[] = getTransfers(); + +const txb = new Transaction(); + +// First, split the gas coin into multiple coins: +const coins = txb.splitCoins( + txb.gas, + transfers.map((transfer) => txb.pure(transfer.amount)) +); + +// Then, create a transfer transaction for each coin: +transfers.forEach((transfer, index) => { + txb.transferObjects([coins[index]], txb.pure(transfer.to)); +}); +``` + +After defining the PTB, you can execute it directly with a `signer` using `signAndExecuteTransaction`: + +```ts +signer.signAndExecuteTransaction({ Transaction: txb }); +``` + +## Defining Inputs + +Inputs allow you to provide external values to PTBs, +such as specifying the amount of IOTA to transfer or passing objects into a Move call. + +There are two primary ways to define inputs: + +- For objects: use the `txb.object(objectId)` function to construct an input containing an object reference. +- For pure values: use the `txb.pure(value, type?)` function to create an input for non-object values. + - If the value is a `Uint8Array`, it's treated as raw bytes and used directly. + - If a type is provided, it's used to generate the BCS serialization layout for the value. If not, the type is automatically determined based on the value. + +## Available Transaction Commands + +IOTA supports the following transaction commands: + +### `txb.splitCoins(coin, amounts)` +Creates new coins with specified amounts, split from the provided coin. Returns the coins for use in subsequent transactions. + +#### Example + +`txb.splitCoins(txb.gas, [txb.pure(100), txb.pure(200)])` + +### `txb.mergeCoins(destinationCoin, sourceCoins)` + +Merges the `sourceCoins` into the `destinationCoin`. + +#### Example + +`txb.mergeCoins(txb.object(coin1), [txb.object(coin2), txb.object(coin3)])` + +### `txb.transferObjects(objects, address)` + +Transfer a list of objects to the specified address. + +#### Example + +`txb.transferObjects([txb.object(thing1), txb.object(thing2)], txb.pure(myAddress))` + +### `txb.moveCall({ target, arguments, typeArguments })` + +Executes a Move call and returns whatever the IOTA Move call returns. + +#### Example + +`txb.moveCall({ target: '0x2::devnet_nft::mint', arguments: [txb.pure(name), txb.pure(description), txb.pure(image)] })` + +### `txb.makeMoveVec({ type, elements })` + +Constructs a vector of objects that can be passed into a `moveCall`. This is required since there's no other way to define a vector as an input. + +#### Example + +`txb.makeMoveVec({ elements: [txb.object(id1), txb.object(id2)] })` + +### `txb.publish(modules, dependencies)` + +Publishes a Move package and returns the upgrade capability object. + +## Using Transaction Results as Arguments + +You can pass the result of a transaction command as an argument in subsequent commands. +Each transaction command method on the transaction builder returns a reference to the transaction result. + +```ts +// Split a coin object from the gas object: +const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]); +// Transfer the resulting coin object: +txb.transferObjects([coin], txb.pure(address)); +``` + +When a transaction command returns multiple results, +you can access a specific result using destructuring or array indices. + +```ts +// Using destructuring (preferred for logical naming): +const [nft1, nft2] = txb.moveCall({ target: "0x2::nft::mint_many" }); +txb.transferObjects([nft1, nft2], txb.pure(address)); + +// Using array indices: +const mintMany = txb.moveCall({ target: "0x2::nft::mint_many" }); +txb.transferObjects([mintMany[0], mintMany[1]], txb.pure(address)); +``` + +## Using the Gas Coin + +With PTBs, you can use the gas payment coin to create coins with a specific balance using [`splitCoin`](programmable-transaction-blocks.mdx#splitcoins). +This is useful for IOTA payments and eliminates the need for upfront coin selection. +You can access the gas coin in a PTB using `txb.gas`, and it's valid as input for any arguments. +However, with `transferObjects`, `txb.gas` must be used by reference. +Practically, this means you can add to the gas coin with `mergeCoins` or borrow it for Move functions with `moveCall`. + +You can also transfer the gas coin using `transferObjects` if you wish to transfer your entire coin balance to another address. + +## Obtaining PTB Bytes + +If you need the PTB bytes instead of signing or executing it, you can use the `build` method on the transaction builder. + +:::tip + +You might need to explicitly call `setSender()` on the PTB to ensure the `sender` field is populated. This is usually done by the signer before signing the transaction but won't happen automatically if you're building the PTB bytes yourself. + +::: + +```ts +const txb = new Transaction(); + +// ... add some transactions... + +await txb.build({ provider }); +``` + +In most cases, building requires your JSON RPC provider to fully resolve input values. + +If you have PTB bytes, you can also convert them back into a `Transaction` class: + +```ts +const bytes = getTransactionBytesFromSomewhere(); +const txb = Transaction.from(bytes); +``` + +## Building Offline + +If you want to build a PTB offline (without a `provider`), you need to fully define all your input values and gas configuration (see the example below). For pure values, you can provide a `Uint8Array` which is used directly in the transaction. For objects, you can use the `Inputs` helper to construct an object reference. + +```ts +import { Inputs } from "@iota/iota-sdk/transactions"; + +// For pure values: +txb.pure(pureValueAsBytes); + +// For owned or immutable objects: +txb.object(Inputs.ObjectRef({ digest, objectId, version })); + +// For shared objects: +txb.object(Inputs.SharedObjectRef({ objectId, initialSharedVersion, mutable })); +``` + +You can then omit the `provider` object when calling `build` on the transaction. If any required data is missing, this will throw an error. + +## Configuring Gas Settings + +The transaction builder comes with default behavior for gas logic, including automatically setting the gas price, budget, and selecting coins for gas payment. This behavior can be customized. + +### Setting Gas Price + +By default, the gas price is set to the network's reference gas price. You can explicitly set the gas price of the PTB by calling `setGasPrice` on the transaction builder. + +```ts +txb.setGasPrice(gasPrice); +``` + +### Setting Gas Budget + +By default, the gas budget is automatically derived by executing a dry run of the PTB beforehand. The dry run's gas consumption is then used to determine a balance for the transaction. You can override this behavior by explicitly setting a gas budget for the transaction using `setGasBudget`. + +:::info + +The gas budget is represented in IOTA and should consider the PTB's gas price. + +::: + +```ts +txb.setGasBudget(gasBudgetAmount); +``` + +### Specifying Gas Payment + +By default, the gas payment is automatically determined by the SDK, +selecting all coins at the provided address that are not used as inputs in the PTB. + +[The selected coins will be merged into a single gas coin before executing the PTB](optimizing-gas-with-coin-merging.mdx), +and all but one of the gas objects will be deleted. The gas coin at index 0 will be the coin into which all others are merged. + +```ts +// Ensure that the coins do not overlap with any input objects for the PTB. +txb.setGasPayment([coin1, coin2]); +``` + +### Integrating with dApps and Wallets + +The Wallet Standard interface now supports the `Transaction` kind directly. All `signTransaction` and `signAndExecuteTransaction` calls from dApps to wallets are expected to provide a `Transaction` class. This PTB class can then be serialized and sent to your wallet for execution. + +To serialize a PTB for sending to a wallet, it's recommended to use the `txb.serialize()` function, +which returns an opaque string representation of the PTB +that can be passed from the wallet standard dApp context to your wallet. +This can then be converted back into a `Transaction` using `Transaction.from()`. + +:::tip + +You should not build the PTB from bytes in the dApp code. +Using `serialize` instead of `build` allows you to build the PTB bytes within the wallet itself, +enabling the wallet to perform gas logic and coin selection as needed. + +::: + +```ts +// Within a dApp +const tx = new Transaction(); +wallet.signTransaction({ Transaction: tx }); + +// Wallet standard code: +function handleSignTransaction(input) { + sendToWalletContext({ Transaction: input.Transaction.serialize() }); +} + +// Within your wallet context: +function handleSignRequest(input) { + const userTx = Transaction.from(input.transaction); +} +``` + +## Creating Sponsored PTBs + +The PTB builder supports sponsored PTBs by using the `onlyTransactionKind` flag when building the PTB. + +```ts +const txb = new Transaction(); +// ... add some transactions... + +const kindBytes = await txb.build({ provider, onlyTransactionKind: true }); + +// Construct a sponsored transaction from the kind bytes: +const sponsoredTxb = Transaction.fromKind(kindBytes); + +// Set the necessary sponsored transaction data: +sponsoredTxb.setSender(sender); +sponsoredTxb.setGasOwner(sponsor); +sponsoredTxb.setGasPayment(sponsorCoins); +``` + + \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/building-ptb.mdx b/docs/content/developer/iota-101/transactions/ptb/building-ptb.mdx deleted file mode 100644 index 3b665c04a0e..00000000000 --- a/docs/content/developer/iota-101/transactions/ptb/building-ptb.mdx +++ /dev/null @@ -1,246 +0,0 @@ ---- -title: Building Programmable Transaction Blocks -description: Using the IOTA TypeScript SDK, you can create programmable transaction blocks to perform multiple commands in a single transaction. ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/ptb/building-ptb.json'; - -This guide explores creating a programmable transaction block (PTB) on IOTA using the TypeScript SDK. For an overview of what a PTB is, see [Programmable Transaction Blocks](prog-txn-blocks.mdx) in the Concepts section. If you don't already have the IOTA TypeScript SDK, follow the [install instructions](../../../../references/ts-sdk/typescript/install.mdx) on the IOTA TypeScript SDK site. - -This example starts by constructing a PTB to send IOTA. If you are familiar with the legacy IOTA transaction types, this is similar to a `payIota` transaction. To construct transactions, import the `Transaction` class, and construct it: - -```ts -import { Transaction } from "@iota/iota-sdk"; -const txb = new Transaction(); -``` - -Using this, you can then add transactions to this PTB. - -```ts -// Create a new coin with balance 100, based on the coins used as gas payment. -// You can define any balance here. -const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]); - -// Transfer the split coin to a specific address. -txb.transferObjects([coin], txb.pure("0xSomeIOTAAddress")); -``` - -You can attach multiple transaction commands of the same type to a PTB as well. For example, to get a list of transfers, and iterate over them to transfer coins to each of them: - -```ts -interface Transfer { - to: string; - amount: number; -} - -// Procure a list of some IOTA transfers to make: -const transfers: Transfer[] = getTransfers(); - -const txb = new Transaction(); - -// First, split the gas coin into multiple coins: -const coins = txb.splitCoins( - txb.gas, - transfers.map((transfer) => txb.pure(transfer.amount)) -); - -// Next, create a transfer transaction for each coin: -transfers.forEach((transfer, index) => { - txb.transferObjects([coins[index]], txb.pure(transfer.to)); -}); -``` - -After you have the PTB defined, you can directly execute it with a `signer` using `signAndExecuteTransaction`. - -```ts -signer.signAndExecuteTransaction({ Transaction: txb }); -``` - -## Constructing Inputs - -Inputs are how you provide external values to PTBs. For example, defining an amount of IOTA to transfer, or which object to pass into a Move call, or a shared object. - -There are currently two ways to define inputs: - -- For objects: the `txb.object(objectId)` function is used to construct an input that contains an object reference. -- For pure values: the `txb.pure(value, type?)` function is used to construct an input for a non-object input. - - If value is a `Uint8Array`, then the value is assumed to be raw bytes and is used directly. - - If type is provided, it's used to generate the BCS serialization layout for the value. If not provided, the type is automatically determined based on the value. - -## Available Transactions - -IOTA supports following transaction commands: - -- `txb.splitCoins(coin, amounts)`: Creates new coins with the defined amounts, split from the provided coin. Returns the coins so that it can be used in subsequent transactions. - - Example: `txb.splitCoins(txb.gas, [txb.pure(100), txb.pure(200)])` -- `txb.mergeCoins(destinationCoin, sourceCoins)`: Merges the sourceCoins into the destinationCoin. - - Example: `txb.mergeCoins(txb.object(coin1), [txb.object(coin2), txb.object(coin3)])` -- `txb.transferObjects(objects, address)`: Transfers a list of objects to the specified address. - - Example: `txb.transferObjects([txb.object(thing1), txb.object(thing2)], txb.pure(myAddress))` -- `txb.moveCall({ target, arguments, typeArguments })`: Executes a Move call. Returns whatever the IOTA Move call returns. - - Example: `txb.moveCall({ target: '0x2::devnet_nft::mint', arguments: [txb.pure(name), txb.pure(description), txb.pure(image)] })` -- `txb.makeMoveVec({ type, elements })`: Constructs a vector of objects that can be passed into a moveCall. This is required as there's no other way to define a vector as an input. - - Example: `txb.makeMoveVec({ elements: [txb.object(id1), txb.object(id2)] })` -- `txb.publish(modules, dependencies)`: Publishes a Move package. Returns the upgrade capability object. - -## Passing Transaction Results As Arguments - -You can use the result of a transaction command as an argument in subsequent transaction commands. Each transaction command method on the transaction builder returns a reference to the transaction result. - -```ts -// Split a coin object off of the gas object: -const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]); -// Transfer the resulting coin object: -txb.transferObjects([coin], txb.pure(address)); -``` - -When a transaction command returns multiple results, you can access the result at a specific index either using destructuring, or array indexes. - -```ts -// Destructuring (preferred, as it gives you logical local names): -const [nft1, nft2] = txb.moveCall({ target: "0x2::nft::mint_many" }); -txb.transferObjects([nft1, nft2], txb.pure(address)); - -// Array indexes: -const mintMany = txb.moveCall({ target: "0x2::nft::mint_many" }); -txb.transferObjects([mintMany[0], mintMany[1]], txb.pure(address)); -``` - -## Use the Gas Coin - -With PTBs, you can use the gas payment coin to construct coins with a set balance using `splitCoin`. This is useful for IOTA payments, and avoids the need for up-front coin selection. You can use `txb.gas` to access the gas coin in a PTB, and it is valid as input for any arguments; with the exception of `transferObjects`, `txb.gas` must be used by-reference. Practically speaking, this means you can also add to the gas coin with `mergeCoins` or borrow it for Move functions with `moveCall`. - -You can also transfer the gas coin using `transferObjects`, in the event that you want to transfer all of your coin balance to another address. - -## Get PTB Bytes - -If you need the PTB bytes, instead of signing or executing the PTB, you can use the `build` method on the transaction builder itself. - -:::tip - -You might need to explicitly call `setSender()` on the PTB to ensure that the `sender` field is populated. This is normally done by the signer before signing the transaction, but will not be done automatically if you're building the PTB bytes yourself. - -::: - -```ts -const txb = new Transaction(); - -// ... add some transactions... - -await txb.build({ provider }); -``` - -In most cases, building requires your JSON RPC provider to fully resolve input values. - -If you have PTB bytes, you can also convert them back into a `Transaction` class: - -```ts -const bytes = getTransactionBytesFromSomewhere(); -const txb = Transaction.from(bytes); -``` - -## Building Offline - -In the event that you want to build a PTB offline (as in with no `provider` required), you need to fully define all of your input values, and gas configuration (see the following example). For pure values, you can provide a `Uint8Array` which is used directly in the transaction. For objects, you can use the `Inputs` helper to construct an object reference. - -```ts -import { Inputs } from "@iota/iota-sdk/transactions"; - -// For pure values: -txb.pure(pureValueAsBytes); - -// For owned or immutable objects: -txb.object(Inputs.ObjectRef({ digest, objectId, version })); - -// For shared objects: -txb.object(Inputs.SharedObjectRef({ objectId, initialSharedVersion, mutable })); -``` - -You can then omit the `provider` object when calling `build` on the transaction. If there is any required data that is missing, this will throw an error. - -## Gas Configuration - -The new transaction builder comes with default behavior for all gas logic, including automatically setting the gas price, budget, and selecting coins to be used as gas. This behavior can be customized. - -### Gas Price - -By default, the gas price is set to the reference gas price of the network. You can also explicitly set the gas price of the PTB by calling `setGasPrice` on the transaction builder. - -```ts -txb.setGasPrice(gasPrice); -``` - -### Budget - -By default, the gas budget is automatically derived by executing a dry-run of the PTB beforehand. The dry run gas consumption is then used to determine a balance for the transaction. You can override this behavior by explicitly setting a gas budget for the transaction, by calling `setGasBudget` on the transaction builder. - -:::info - -The gas budget is represented in IOTA, and should take the gas price of the PTB into account. - -::: - -```ts -txb.setGasBudget(gasBudgetAmount); -``` - -### Gas Payment - -By default, the gas payment is automatically determined by the SDK. The SDK selects all coins at the provided address that are not used as inputs in the PTB. - -The list of coins used as gas payment will be merged down into a single gas coin before executing the PTB, and all but one of the gas objects will be deleted. The gas coin at the 0-index will be the coin that all others are merged into. - -```ts -// NOTE: You need to ensure that the coins do not overlap with any -// of the input objects for the PTB. -txb.setGasPayment([coin1, coin2]); -``` - -### dApp / Wallet Integration - -The Wallet Standard interface has been updated to support the `Transaction` kind directly. All `signTransaction` and `signAndExecuteTransaction` calls from dApps into wallets is expected to provide a `Transaction` class. This PTB class can then be serialized and sent to your wallet for execution. - -To serialize a PTB for sending to a wallet, IOTA recommends using the `txb.serialize()` function, which returns an opaque string representation of the PTB that can be passed from the wallet standard dApp context to your wallet. This can then be converted back into a `Transaction` using `Transaction.from()`. - -:::tip - -You should not build the PTB from bytes in the dApp code. Using `serialize` instead of `build` allows you to build the PTB bytes within the wallet itself. This allows the wallet to perform gas logic and coin selection as needed. - -::: - -```ts -// Within a dApp -const tx = new Transaction(); -wallet.signTransaction({ Transaction: tx }); - -// Your wallet standard code: -function handleSignTransaction(input) { - sendToWalletContext({ Transaction: input.Transaction.serialize() }); -} - -// Within your wallet context: -function handleSignRequest(input) { - const userTx = Transaction.from(input.transaction); -} -``` - -## Sponsored PTBs - -The PTB builder can support sponsored PTBs by using the `onlyTransactionKind` flag when building the PTB. - -```ts -const txb = new Transaction(); -// ... add some transactions... - -const kindBytes = await txb.build({ provider, onlyTransactionKind: true }); - -// Construct a sponsored transaction from the kind bytes: -const sponsoredTxb = Transaction.fromKind(kindBytes); - -// You can now set the sponsored transaction data that is required: -sponsoredTxb.setSender(sender); -sponsoredTxb.setGasOwner(sponsor); -sponsoredTxb.setGasPayment(sponsorCoins); -``` - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/coin-management.mdx b/docs/content/developer/iota-101/transactions/ptb/coin-management.mdx new file mode 100644 index 00000000000..e942cff51f2 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/coin-management.mdx @@ -0,0 +1,69 @@ +--- +description: Understand how to handle owned coins in IOTA transactions and programmable transaction block development. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/ptb/coin-management.json'; + +# Managing Owned Coins in IOTA + +In IOTA programming, effectively managing owned coins is essential for seamless transaction execution. +This guide explores the concept of [owned objects](../../objects/object-ownership/address-owned.mdx), +how they differ from traditional account balances, and best practices for coin management in IOTA. + +## Understanding Owned Objects + +IOTA introduces the concept of [address-owned objects](../../objects/object-ownership/address-owned.mdx), +which allows for highly parallelizable transactions and logically maps to assets exclusively owned by an individual. +Coins in IOTA are examples of these owned objects, similar to holding cash rather than maintaining a bank balance. + +Unlike other blockchains that use a single balance per account, +IOTA's approach means you might own multiple coins as separate objects . +This paradigm shift requires a different strategy for managing your assets. + +## The Importance of Coin Management + +Owning multiple coins means you may need to merge them to meet the value required for certain transactions. +[Merging coins](#coin-merging-overview) becomes necessary when the amount needed exceeds any single coin you possess, +making coin management a crucial step in transaction preparation. + +## Using the IOTA SDKs + +The IOTA SDKs for [TypeScript](../../../../references/ts-sdk/typescript/index.mdx) and [Rust](../../../../references/rust-sdk.mdx) handle coin management automatically, +merging coins as needed and assuming sequential transaction execution. +This automation simplifies the process for most use cases where high concurrency isn't a concern. + +## Coin Merging Overview + +IOTA allows you to provide multiple coins as payment in a transaction. +This feature, known as [coin merging](optimizing-gas-with-coin-merging.mdx), +automatically combines these coins into one, +presenting your [programmable transaction blocks (PTBs)](programmable-transaction-blocks) with a single gas coin +usable for various purposes beyond just paying for gas. + +You can supply numerous coins (up to a protocol-defined limit), which are merged into the first coin provided. +After deducting the gas budget, the remaining amount is available within the transaction and returned if unused. + +## Managing Generic Coins + +While coin merging works seamlessly for `Coin` objects used for gas payments, +other coin types require manual management. +PTBs offer commands like [`mergeCoins`](programmable-transaction-blocks#mergecoins) to combine multiple coins, +and [`splitCoins`](programmable-transaction-blocks#splitcoins) to divide them, giving you control over your coin distribution. + +These operations are cost-effective but necessitate awareness of your specific needs and coin holdings. + +## Addressing Concurrency Challenges + +When high concurrency is required, merging all your `Coin` into a single coin can create bottlenecks. +Since each coin is an owned object, it's locked during a transaction, preventing simultaneous use in other transactions. +Attempting to use the same coin concurrently can lead to conflicts and render the coin unusable until the epoch ends. + +To enable concurrent transactions, +consider splitting a coin into multiple coins corresponding to the number of transactions, +or using different coins for each transaction without overlap. + +Concurrency introduces additional complexities, +such as performance impacts from multiple network interactions with full nodes during transaction creation and submission. +Careful planning is essential, and strategies should be tailored to your specific scenarios. + + diff --git a/docs/content/developer/iota-101/transactions/ptb/coin-mgt.mdx b/docs/content/developer/iota-101/transactions/ptb/coin-mgt.mdx deleted file mode 100644 index 1f3c41edc9f..00000000000 --- a/docs/content/developer/iota-101/transactions/ptb/coin-mgt.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Coin Management -description: Because IOTA uses Coins as owned objects for transactions, you need to explicitly manage them in your programmable transaction block development. ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/ptb/coin-mgt.json'; - -A key concept when programming on IOTA is that of owned objects. Address-owned objects are important in that they allow for highly parallelizable transactions. And they also logically map to assets or resources that someone exclusively owns. Coins are a typical case of owned object usage, with cash being a real-life reference. The owned objects paradigm, however, and particularly as related to coins, is somewhat of a divergence from other blockchains which have a concept of balance. In other words, in other systems, especially account based systems, coins are held in a single location (field) which can be thought of as a balance in a bank account. - -Because IOTA uses owned objects instead of a balance, it is common to own a number of coins, at times even a significant number of them. Some scenarios necessitate merging some or all of those coins into a single object. At times, merging coins together might even be required because the amount necessary to execute a transaction is more than any single coin the sender owns, thus making merging an inevitable step. - -## SDK usage - -The IOTA SDKs ([TypeScript](../../../../references/ts-sdk/typescript/index.mdx) and [Rust](../../../../references/rust-sdk.mdx)) manage coins on your behalf, removing the overhead of having to deal with coin management manually. The SDKs attempt to merge coins whenever possible and assume that transactions are executed in sequence. That's a reasonable assumption with wallet-based transactions and for common scenarios in general. IOTA recommends relying on this feature if you do not have a need for heavy parallel or concurrent execution. - -## Gas Smashing - -When executing a transaction IOTA allows you to provide a number of coins as payment. In other words, the payment can be a vector of coins rather than a single coin. That feature, known as gas smashing, performs merging of coins automatically, and presents the PTBs you write with a single gas coin that can be used for other purposes besides just gas. - -Basically, you can provide as many coins as you want (with a max limit defined in the protocol configuration) and have all of them merged (smashed) into the first coin provided as payment. That coin, minus the gas budget, is then available inside the transaction and can be used in any command. If the coin is unused it is returned to the user. - -Gas smashing is an important feature - and a key concept to understand - to have for the optimal management of coins. See [Gas Smashing](../gas-smashing.mdx) for more details. - -## Generic coins - -Gas smashing works well for `Coin` objects, which is the only coin type that can be used for gas payment. - -Any other coin type requires explicit management from users. PTBs offer a `mergeCoins` command that you can use to combine multiple coins into a single one. And a `splitCoins` as the complementary operation to break them up. - -From a cost perspective, those are very cheap transactions, however they require a user to be aware of their coin distribution and their own needs. - -## Concurrency - -Merging coins, and particularly `Coin`, into a single coin or a very small number of coins might prove problematic in scenarios where heavy or high concurrency is required. - -If you merge all `Coin` into a single one, you would need to sequentially submit every transaction. The coin - being an owned object - would have to be provided with a version and it would be locked by the system when signing a transaction, effectively making it impossible to use it in any other transaction until the one that locked it was executed. Moreover, an attempt to sign multiple transactions with the same coin might result in equivocation and the coin being unusable and locked until the end of the epoch. - -So when you require heavy concurrency, you should first split a coin into as many coins as the number of transactions to execute concurrently. Alternatively, you could provide multiple and different coins (gas smashing) to the different transactions. It is critically important that the set of coins you use in the different transactions has no intersection at all. - -The possible pitfalls in dealing with heavy concurrency are many. Concurrency in transaction execution is not the only performance bottleneck. In creating and submitting a transaction, several round trips with a Full node might be required to discover and fetch the right objects, and to dry run a transaction. Those round trips might affect performance significantly. - -Concurrency is a difficult subject and is beyond the scope of this documentation. You must take maximum care when dealing with coin management in the face of concurrency, and the right strategy is often tied to the specific scenario, rather than universally available. - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx b/docs/content/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx new file mode 100644 index 00000000000..650588d0b2c --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx @@ -0,0 +1,64 @@ +--- +description: Learn how to manage multiple IOTA coins by combining them to pay gas fees efficiently. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.json'; + +# Optimizing Gas Fees with Coin Merging + +In the IOTA blockchain, every transaction incurs a gas fee that must be paid for successful execution. +Coin merging allows you to pay this gas fee using multiple coins combined into one, +rather than relying on a single coin. +This method is particularly useful when you have several coins with small denominations or wish +to reduce the number of individual IOTA coins in your account. +Coin merging enhances coin management, especially when used with the `GasCoin` argument in [programmable transaction blocks (PTBs)](programmable-transaction-blocks). + +## How Coin Merging Works + +When you include multiple coins to cover the gas fee in a transaction, +IOTA automatically merges them into a single coin during execution. +This merging occurs regardless of the individual coin amounts or the specified gas budget, +as long as it falls within the allowed gas limits. +The gas fee is then deducted from this combined coin, +even if the transaction fails to execute (for example, due to an execution error). +This means that after the transaction, the coins you provided for gas remain merged. + +Coin merging is an efficient strategy for managing your coins by consolidating smaller amounts into a single coin. +You can use this merged coin not only to pay for gas fees but also for other operations within the same transaction. +For instance, you can merge multiple coins to cover the gas fee for a PTB, +and then have that PTB withdraw from the `GasCoin`. +PTBs offer a special argument that references the coin used for gas payment after the gas fee has been deducted, +allowing you to transfer the remaining IOTA to another address. +Since coin merging happens automatically when you supply multiple gas coins, +you can combine coin management with other transactions seamlessly. + +It's important to note that IOTA sets a maximum limit of 256 coins that can be merged in a single PTB. +If you attempt to merge more than this number, the transaction will not be processed. +Additionally, during the merging process, all coins except the first one are deleted. +This deletion often results in a storage rebate due to the removal of coin objects. +While you cannot use this rebate to pay for the current transaction's gas fee, +as it isn't credited until after execution, +it may result in a refund after the transaction completes. +The refund and any remaining balance after deducting the gas fee will reside in the first coin +you provided in the transaction. + +## Unexpected Refunds When Running Out of Gas + +Because coins are merged regardless of whether a transaction succeeds, +storage rebates can lead to scenarios where a transaction runs out of gas but still results in a net refund. +For example, suppose you have a transaction `T` with a gas budget of `5000`, +and you provide coins `C1`, `C2`, `C3`, `C4`, and `C5` with values `1000`, `2000`, `3000`, +`4000`, and `5000` respectively to pay for gas. +If each deleted coin object provides a storage rebate of `2000`, +and the actual gas fee for executing `T` exceeds the `5000` gas budget (causing `T` to run out of gas), the execution status will be `OutOfGas`. + +After execution, the balance of coin `C1` would be calculated as: + +- Initial total coin value: `1000 + 2000 + 3000 + 4000 + 5000 = 15,000` +- Gas fee deducted: `-5000` +- Storage rebate from deleted coins: `2000 * 4 = 8000` (since four coins were deleted) +- Final balance: `15,000 - 5000 + 8000 = 18,000` + +In this case, even though the transaction failed due to insufficient gas, you receive a net refund of `3000` (since you end up with `18,000` in `C1`). + + diff --git a/docs/content/developer/iota-101/transactions/ptb/prog-txn-blocks.mdx b/docs/content/developer/iota-101/transactions/ptb/prog-txn-blocks.mdx deleted file mode 100644 index 5a584716642..00000000000 --- a/docs/content/developer/iota-101/transactions/ptb/prog-txn-blocks.mdx +++ /dev/null @@ -1,447 +0,0 @@ ---- -title: Programmable Transaction Blocks -description: Programmable transaction blocks are a group of commands that complete a transaction on IOTA. ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/ptb/prog-txn-blocks.json'; - -On IOTA, a transaction is more than a basic record of the flow of assets. Transactions on IOTA are composed of a number of commands that execute on inputs to define the result of the transaction. Termed programmable transaction blocks (PTBs), these groups of commands define all user transactions on IOTA. PTBs allow a user to call multiple Move functions, manage their objects, and manage their coins in a single transaction--without publishing a new Move package. Designed with automation and transaction builders in mind, PTBs are a lightweight and flexible way of generating transactions. More intricate programming patterns, such as loops, are not supported, however, and in those cases you must publish a new Move package. - -As mentioned, each PTB is comprised of individual transaction commands (sometimes referred to simply as transactions or commands). Each transaction command executes in order, and you can use the results from one transaction command in any subsequent transaction command. The effects, specifically object modifications or transfers, of all transaction commands in a block are applied atomically at the end of the transaction. If one transaction command fails, the entire block fails and no effects from the commands are applied. - -A PTB can perform up to 1,024 unique operations in a single execution, whereas transactions on traditional blockchains would require 1,024 individual executions to accomplish the same result. The structure also promotes cheaper gas fees. The cost of facilitating individual transactions is always more than the cost of those same transactions blocked together in a PTB. - -The remainder of this topic covers the semantics of the execution of the transaction commands. It assumes familiarity with the IOTA object model and the Move language. For more information on those topics, see the following documents: - -- [Object model](../../objects/object-model.mdx) -- [Move Concepts](../../move-overview/move-overview.mdx) - -## Transaction type - -There are two parts of a PTB that are relevant to execution semantics. Other transaction information, such as the transaction sender or the gas limit, might be referenced but are out of scope. The structure of a PTB is: - -```rust -{ - inputs: [Input], - commands: [Command], -} -``` - -Looking closer at the two main components: -- The `inputs` value is a vector of arguments, `[Input]`. These arguments are either objects or pure values that you can use in the commands. The objects are either owned by the sender or are shared/immutable objects. The pure values represent simple Move values, such as `u64` or `String` values, which you can be construct purely from their bytes. For historical reasons, `Input` is `CallArg` in the Rust implementation. -- The `commands` value is a vector of commands, `[Command]`. The possible commands are: - - `TransferObjects` sends multiple (one or more) objects to a specified address. - - `SplitCoins` splits off multiple (one or more) coins from a single coin. It can be any `iota::coin::Coin<_>` object. - - `MergeCoins` merges multiple (one or more) coins into a single coin. Any `iota::coin::Coin<_>` objects can be merged, as long as they are all of the same type. - - `MakeMoveVec` creates a vector (potentially empty) of Move values. This is used primarily to construct vectors of Move values to be used as arguments to `MoveCall`. - - `MoveCall` invokes either an `entry` or a `public` Move function in a published package. - - `Publish` creates a new package and calls the `init` function of each module in the package. - - `Upgrade` upgrades an existing package. The upgrade is gated by the `iota::package::UpgradeCap` for that package. - -## Inputs and results - -Inputs and results are the two types of values you can use in transaction commands. Inputs are the values that are provided to the PTB, and results are the values that are produced by the PTB commands. The inputs are either objects or simple Move values, and the results are arbitrary Move values (including objects). - -The inputs and results can be seen as populating an array of values. For inputs, there is a single array, but for results, there is an array for each individual transaction command, creating a 2D-array of result values. You can access these values by borrowing (mutably or immutably), by copying (if the type permits), or by moving (which takes the value out of the array without re-indexing). - -### Inputs - -Input arguments to a PTB are broadly categorized as either objects or pure values. The direct implementation of these arguments is often obscured by transaction builders or SDKs. This section describes information or data the IOTA network needs when specifying the list of inputs, `[Input]`. Each `Input` is either an object, `Input::Object(ObjectArg)`, which contains the necessary metadata to specify to object being used, or a pure value, `Input::Pure(PureArg)`, which contains the bytes of the value. - -For object inputs, the metadata needed differs depending on the type of [ownership of the object](../../objects/object-ownership/object-ownership.mdx). The data for the `ObjectArg` enum follows: - -If the object is owned by an address (or it is immutable), then use `ObjectArg::ImmOrOwnedObject(ObjectID, SequenceNumber, ObjectDigest)`. The triple respectively specifies the object's ID, its sequence number (also known as its version), and the digest of the object's data. - -If an object is shared, then use `Object::SharedObject { id: ObjectID, initial_shared_version: SequenceNumber, mutable: bool }`. Unlike `ImmOrOwnedObject`, a shared objects version and digest are determined by the network's consensus protocol. The `initial_shared_version` is the version of the object when it was first shared, which is used by consensus when it has not yet seen a transaction with that object. While all shared objects _can_ be mutated, the `mutable` flag indicates whether the object is to be used mutably in this transaction. In the case where the `mutable` flag is set to `false`, the object is read-only, and the system can schedule other read-only transactions in parallel. - -If the object is owned by another object, as in it was sent to an object's ID via the `TransferObjects` command or the `iota::transfer::transfer` function, then use `ObjectArg::Receiving(ObjectID, SequenceNumber, ObjectDigest)`. The object data is the same as for the `ImmOrOwnedObject` case. - -For pure inputs, the only data provided is the [BCS](/references/framework/iota-framework/bcs.mdx) bytes, which are deserialized to construct Move values. Not all Move values can be constructed from BCS bytes. This means that even if the bytes match the expected layout for a given Move type, they cannot be deserialized into a value of that type unless the type is one of the types permitted for `Pure` values. The following types are allowed to be used with pure values: - -- All primitive types: - - `u8` - - `u16` - - `u32` - - `u64` - - `u128` - - `u256` - - `bool` - - `address` -- A string, either an ASCII string (`std::ascii::String`) or UTF8 string (`std::string::String`). In either case, the bytes are validated to be a valid string with the respective encoding. -- An object ID `iota::object::ID`. -- A vector, `vector`, where `T` is a valid type for a pure input, checked recursively. -- An option, `std::option::Option`, where `T` is a valid type for a pure input, checked recursively. - -Interestingly, the bytes are not validated until the type is specified in a command, for example in `MoveCall` or `MakeMoveVec`. This means that a given pure input could be used to instantiate Move values of several types. See the [Arguments section](#arguments) for more details. - -### Results - -Each transaction command produces a (possibly empty) array of values. The type of the value can be any arbitrary Move type, so unlike inputs, the values are not limited to objects or pure values. The number of results generated and their types are specific to each transaction command. The specifics for each command can be found in the section for that command, but in summary: - -- `MoveCall`: the number of results and their types are determined by the Move function being called. Move functions that return references are not supported at this time. -- `SplitCoins`: produces (one or more) coins from a single coin. The type of each coin is `iota::coin::Coin` where the specific coin type `T` matches the coin being split. -- `Publish`: returns the upgrade capability, `iota::package::UpgradeCap`, for the newly published package. -- `Upgrade`: returns the upgrade receipt, `iota::package::UpgradeReceipt`, for the upgraded package. -- `TransferObjects` and `MergeCoins` do not produce any results (an empty result vector). - -### Argument structure and usage - -Each command takes `Argument`s that specify the input or result being used. The usage (by-reference or by-value) is inferred based on the type of the argument and the expected argument of the command. First, examine the structure of the `Argument` enum. - -- `Input(u16)` is an input argument, where the `u16` is the index of the input in the input vector. For example, given an input vector of `[Object1, Object2, Pure1, Object3]`, `Object1` is accessed with `Input(0)` and `Pure1` is accessed with `Input(2)`. -- `GasCoin` is a special input argument representing the object for the `IOTA` coin used to pay for gas. It is kept separate from the other inputs because the gas coin is always present in each transaction and has special restrictions (see below) not present for other inputs. Additionally, the gas coin being separate makes its usage explicit, which is helpful for sponsored transactions where the sponsor might not want the sender to use the gas coin for anything other than gas. - - The gas coin cannot be taken by-value except with the `TransferObjects` command. If you need an owned version of the gas coin, you can first use `SplitCoins` to split off a single coin. - - This limitation exists to make it easy for the remaining gas to be returned to the coin at the end of execution. In other words, if the gas coin was wrapped or deleted, then there would not be an obvious spot for the excess gas to be returned. See the [Execution section](#execution) for more details. - -- `NestedResult(u16, u16)` uses the value from a previous command. The first `u16` is the index of the command in the command vector, and the second `u16` is the index of the result in the result vector of that command. For example, given a command vector of `[MoveCall1, MoveCall2, TransferObjects]` where `MoveCall2` has a result vector of `[Value1, Value2]`, `Value1` would be accessed with `NestedResult(1, 0)` and `Value2` would be accessed with `NestedResult(1, 1)`. -- `Result(u16)` is a special form of `NestedResult` where `Result(i)` is roughly equivalent to `NestedResult(i, 0)`. Unlike `NestedResult(i, 0)`, `Result(i)`, however, this errors if the result array at index `i` is empty or has more than one value. The ultimate intention of `Result` is to allow accessing the entire result array, but that is not yet supported. So in its current state, `NestedResult` can be used instead of `Result` in all circumstances. - -## Execution - -For the execution of PTBs, the input vector is populated by the input objects or pure value bytes. The transaction commands are then executed in order, and the results are stored in the result vector. Finally, the effects of the transaction are applied atomically. The following sections describe each aspect of execution in greater detail. - -### Start of execution - -At the beginning of execution, the PTB runtime takes the already loaded input objects and loads them into the input array. The objects are already verified by the network, checking rules like existence and valid ownership. The pure value bytes are also loaded into the array but not validated until usage. - -The most important thing to note at this stage is the effects on the gas coin. At the beginning of execution, the maximum gas budget (in terms of `IOTA`) is withdrawn from the gas coin. Any unused gas is returned to the gas coin at the end of execution, even if the coin has changed owners. - -### Executing a transaction command - -Each transaction command is then executed in order. First, examine the rules around arguments, which are shared by all commands. - -#### Arguments - -You can use each argument by-reference or by-value. The usage is based on the type of the argument and the type signature of the command. - - If the signature expects an `&mut T`, the runtime checks the argument has type `T` and it is then mutably borrowed. - - If the signature expects an `&T`, the runtime checks the argument has type `T` and it is then immutably borrowed. - - If the signature expects a `T`, the runtime checks the argument has type `T` and it is copied if `T: copy` and moved otherwise. No object in IOTA has `copy` because the unique ID field `iota::object::UID` present in all objects does not have the `copy` ability. - -The transaction fails if an argument is used in any form after being moved. There is no way to restore an argument to its position (its input or result index) after it is moved. - -If an argument is copied but does not have the `drop` ability, then the last usage is inferred to be a move. As a result, if an argument has `copy` and does not have `drop`, the last usage _must_ be by value. Otherwise, the transaction will fail because a value without `drop` has not been used. - -The borrowing of arguments has other rules to ensure unique safe usage of an argument by reference. If an argument is: - - Mutably borrowed, there must be no outstanding borrows. Duplicate borrows with an outstanding mutable borrow could lead to dangling references (references that point to invalid memory). - - Immutably borrowed, there must be no outstanding mutable borrows. Duplicate immutable borrows are allowed. - - Moved, there must be no outstanding borrows. Moving a borrowed value would dangle those outstanding borrows, making them unsafe. - - Copied, there can be outstanding borrows, mutable or immutable. While it might lead to some unexpected results in some cases, there is no safety concern. - -Object inputs have the type of their object `T` as you might expect. However, for `ObjectArg::Receiving` inputs, the object type `T` is instead wrapped as `iota::transfer::Receiving`. This is because the object is not owned by the sender, but instead by another object. And to prove ownership with that parent object, you call the `iota::transfer::receive` function to remove the wrapper. - -The `GasCoin` has special restrictions on being used by-value (moved). You can only use it by-value with the `TransferObjects` command. - -Shared objects also have restrictions on being used by-value. These restrictions exist to ensure that at the end of the transaction the shared object is either still shared or deleted. A shared object cannot be unshared (having the owner changed) and it cannot be wrapped. A shared object: - - Marked as not `mutable` (being used read-only) cannot be used by value. - - Cannot be transferred or frozen. These checks are not done dynamically, however, but rather at the end of the transaction only. For example, `TransferObjects` succeeds if passed a shared object, but at the end of execution the transaction fails. - - Can be wrapped and can become a dynamic field transiently, but by the end of the transaction it must be re-shared or deleted. - -Pure values are not type checked until their usage. When checking if a pure value has type `T`, it is checked whether `T` is a valid type for a pure value (see the previous list). If it is, the bytes are then validated. You can use a pure value with multiple types as long as the bytes are valid for each type. For example, you can use a string as an ASCII string `std::ascii::String` and as a UTF8 string `std::string::String`. However, after you mutably borrow the pure value, the type becomes fixed, and all future usages must be with that type. - -#### `TransferObjects` - -The command has the form `TransferObjects(ObjectArgs, AddressArg)` where `ObjectArgs` is a vector of objects and `AddressArg` is the address the objects are sent to. - -- Each argument `ObjectArgs: [Argument]` must be an object, however, the objects do not need to have the same type. -- The address argument `AddressArg: Argument` must be an address, which could come from a `Pure` input or a result. -- All arguments, objects and address, are taken by value. -- The command does not produce any results (an empty result vector). -- While the signature of this command cannot be expressed in Move, you can think of it roughly as having the signature `(vector, address): ()` where `forall T: key + store. T` is indicating that the `vector` is a heterogeneous vector of objects. - -#### `SplitCoins` - -The command has the form `SplitCoins(CoinArg, AmountArgs)` where `CoinArg` is the coin being split and `AmountArgs` is a vector of amounts to split off. - -- When the transaction is signed, the network verifies that the `ToMergeArgs` is non-empty. -- The coin argument `CoinArg: Argument` must be a coin of type `iota::coin::Coin` where `T` is the type of the coin being split. It can be any coin type and is not limited to `IOTA` coins. -- The amount arguments `AmountArgs: [Argument]` must be `u64` values, which could come from a `Pure` input or a result. -- The coin argument `CoinArg` is taken by mutable reference. -- The amount arguments `AmountArgs` are taken by value (copied). -- The result of the command is a vector of coins, `iota::coin::Coin`. The coin type `T` is the same as the coin being split, and the number of results matches the number of arguments -- For a rough signature expressed in Move, it is similar to a function `(coin: &mut iota::coin::Coin, amounts: vector): vector>` where the result `vector` is guaranteed to have the same length as the `amounts` vector. - -#### `MergeCoins` - -The command has the form `MergeCoins(CoinArg, ToMergeArgs)` where the `CoinArg` is the target coin in which the `ToMergeArgs` coins are merged into. In other words, you merge multiple coins (`ToMergeArgs`) into a single coin (`CoinArg`). - -- When the transaction is signed, the network verifies that the AmountArgs is non-empty. -- The coin argument `CoinArg: Argument` must be a coin of type `iota::coin::Coin` where `T` is the type of the coin being merged. It can be any coin type and is not limited to `IOTA` coins. -- The coin arguments `ToMergeArgs: [Argument]` must be `iota::coin::Coin` values where the `T` is the same type as the `CoinArg`. -- The coin argument `CoinArg` is taken by mutable reference. -- The merge arguments `ToMergeArgs` are taken by value (moved). -- The command does not produce any results (an empty result vector). -- For a rough signature expressed in Move, it is similar to a function `(coin: &mut iota::coin::Coin, to_merge: vector>): ()` - -#### `MakeMoveVec` - -The command has the form `MakeMoveVec(VecTypeOption, Args)` where `VecTypeOption` is an optional argument specifying the type of the elements in the vector being constructed and `Args` is a vector of arguments to be used as elements in the vector. - -- When the transaction is signed, the network verifies that if that the type must be specified for an empty vector of `Args`. -- The type `VecTypeOption: Option` is an optional argument specifying the type of the elements in the vector being constructed. The `TypeTag` is a Move type for the elements in the vector, i.e. the `T` in the produced `vector`. - - The type does not have to be specified for an object vector--when `T: key`. - - The type _must_ be specified if the type is not an object type or when the vector is empty. -- The arguments `Args: [Argument]` are the elements of the vector. The arguments can be any type, including objects, pure values, or results from previous commands. -- The arguments `Args` are taken by value. Copied if `T: copy` and moved otherwise. -- The command produces a _single_ result of type `vector`. The elements of the vector cannot then be accessed individually using `NestedResult`. Instead, the entire vector must be used as an argument to another command. If you wish to access the elements individually, you can use the `MoveCall` command and do so inside of Move code. -- While the signature of this command cannot be expressed in Move, you can think of it roughly as having the signature `(T...): vector` where `T...` indicates a variadic number of arguments of type `T`. - -#### `MoveCall` - -This command has the form `MoveCall(Package, Module, Function, TypeArgs, Args)` where `Package::Module::Function` combine to specify the Move function being called, `TypeArgs` is a vector of type arguments to that function, and `Args` is a vector of arguments for the Move function. - -- The package `Package: ObjectID` is the Object ID of the package containing the module being called. -- The module `Module: String` is the name of the module containing the function being called. -- The function `Function: String` is the name of the function being called. -- The type arguments `TypeArgs: [TypeTag]` are the type arguments to the function being called. They must satisfy the constraints of the type parameters for the function. -- The arguments `Args: [Argument]` are the arguments to the function being called. The arguments must be valid for the parameters as specified in the function's signature. -- Unlike the other commands, the usage of the arguments and the number of results are dynamic in that they both depend on the signature of the Move function being called. - -#### `Publish` - -The command has the form `Publish(ModuleBytes, TransitiveDependencies)` where `ModuleBytes` are the bytes of the module being published and `TransitiveDependencies` is a vector of package Object ID dependencies to link against. - -When the transaction is signed, the network verifies that the `ModuleBytes` are not empty. The module bytes `ModuleBytes: [[u8]]` contain the bytes of the modules being published with each `[u8]` element is a module. - -The transitive dependencies `TransitiveDependencies: [ObjectID]` are the Object IDs of the packages that the new package depends on. While each module indicates the packages used as dependencies, the transitive object IDs must be provided to select the version of those packages. In other words, these object IDs are used to select the version of the packages marked as dependencies in the modules. - -After the modules in the package are verified, the `init` function of each module is called in same order as the module byte vector `ModuleBytes`. - -The command produces a single result of type `iota::package::UpgradeCap`, which is the upgrade capability for the newly published package. - -#### `Upgrade` - -The command has the form `Upgrade(ModuleBytes, TransitiveDependencies, Package, UpgradeTicket)`, where the `Package` indicates the object ID of the package being upgraded. The `ModuleBytes` and `TransitiveDependencies` work similarly as the `Publish` command. - -For details on the `ModuleBytes` and `TransitiveDependencies`, see the [`Publish` command](#publish). Note though, that no `init` functions are called for the upgraded modules. - -The `Package: ObjectID` is the Object ID of the package being upgraded. The package must exist and be the latest version. - -The `UpgradeTicket: iota::package::UpgradeTicket` is the upgrade ticket for the package being upgraded and is generated from the `iota::package::UpgradeCap`. The ticket is taken by value (moved). - -The command produces a single result type `iota::package::UpgradeReceipt` which provides proof for that upgrade. For more details on upgrades, see [Upgrading Packages](../../move-overview/package-upgrades/upgrade.mdx). - -### End of execution - -At the end of execution, the remaining values are checked and effects for the transaction are calculated. - -For inputs, the following checks are performed: - - Any remaining immutable or readonly input objects are skipped since no modifications have been made to them. - - Any remaining mutable input objects are returned to their original owners--if they were shared they remain shared, if they were owned they remain owned. - - Any remaining pure input values are dropped. Note that pure input values must have `copy` and `drop` since all permissible types for those values have `copy` and `drop`. - - For any shared object you must also check that it has only been deleted or re-shared. Any other operation (wrap, transfer, freezing, and so on) results in an error. - -For results, the following checks are performed: - - Any remaining result with the `drop` ability is dropped. - - If the value has `copy` but not `drop`, it's last usage must have been by-value. In that way, it's last usage is treated as a move. - - Otherwise, an error is given because there is an unused value without `drop`. - -Any remaining IOTA deducted from the gas coin at the beginning of execution is returned to the coin, even if the owner has changed. In other words, the maximum possible gas is deducted at the beginning of execution, and then the unused gas is returned at the end of execution (all in IOTA). Because you can take the gas coin only by-value with `TransferObjects`, it will not have been wrapped or deleted. - -The total effects (which contain the created, mutated, and deleted objects) are then passed out of the execution layer and are applied by the IOTA network. - -## Example - -Let's walk through an example of a PTB's execution. While this example is not exhaustive in demonstrating all the rules, it does show the general flow of execution. - -Suppose you want to buy two items from a marketplace costing `100 NANOS`. You keep one for yourself, and then send the object and the remaining coin to a friend at address `0x808`. You can do that all in one PTB: - -```rust -{ - inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Object(SharedObject { /* Marketplace shared object */ id: market_id, ... }), - Pure(/* 100u64 BCS bytes */ ...), - ] - commands: [ - SplitCoins(GasCoin, [Input(2)]), - MoveCall("some_package", "some_marketplace", "buy_two", [], [Input(1), NestedResult(0, 0)]), - TransferObjects([GasCoin, NestedResult(1, 0)], Input(0)), - MoveCall("iota", "tx_context", "sender", [], []), - TransferObjects([NestedResult(1, 1)], NestedResult(3, 0)), - ] -} -``` - -The inputs include the friend's address, the marketplace object, and the value for the coin split. For the commands, split off the coin, call the market place function, send the gas coin and one object, grab your address (via `iota::tx_context::sender`), and then send the remaining object to yourself. For simplicity, the documentation refers to the package names by name, but note that in practice they are referenced by the package's Object ID. - -To walk through this, first look at the memory locations, for the gas object, inputs, and results - -```rust -Gas Coin: iota::coin::Coin { id: gas_coin, balance: iota::balance::Balance { value: 1_000_000u64 } } -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - some_package::some_marketplace::Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [] -``` - -Here you have two objects loaded so far, the gas coin with a value of `1_000_000u64` and the marketplace object of type `some_package::some_marketplace::Marketplace` (these names and representations are shortened for simplicity going forward). The pure arguments are not loaded, and are present as BCS bytes. - -Note that while gas is deducted at each command, that aspect of execution is not demonstrated in detail. - -### Before commands: start of execution - -Before execution, remove the gas budget from the gas coin. Assume a gas budget of `500_000` so the gas coin now has a value of `500_000u64`. - -```rust -Gas Coin: Coin { id: gas_coin, ... value: 500_000u64 ... } // The maximum gas is deducted -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [] -``` - -Now you can execute the commands. - -### Command 0: `SplitCoins` - -The first command `SplitCoins(GasCoin, [Input(2)])` accesses the gas coin by mutable reference and loads the pure argument at `Input(2)` as a `u64` value of `100u64`. Because `u64` has the `copy` ability, you do not move the `Pure` input at `Input(2)`. Instead, the bytes are copied out. - -For the result, a new coin object is made. - -This gives us updated memory locations of - -```rust -Gas Coin: Coin { id: gas_coin, ... value: 499_900u64 ... } -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [ - [Coin { id: new_coin, value: 100u64 ... }], // The result of SplitCoins -], -``` - -### Command 1: `MoveCall` - -Now the command, `MoveCall("some_package", "some_marketplace", "buy_two", [], [Input(1), NestedResult(0, 0)])`. Call the function `some_package::some_marketplace::buy_two` with the arguments `Input(1)` and `NestedResult(0, 0)`. To determine how they are used, you need to look at the function's signature. For this example, assume the signature is - -```rust -entry fun buy_two( - marketplace: &mut Marketplace, - coin: Coin, - ctx: &mut TxContext, -): (Item, Item) -``` -where `Item` is the type of the two objects being sold. - -Since the `marketplace` parameter has type `&mut Marketplace`, use `Input(1)` by mutable reference. Assume some modifications are being made into the value of the `Marketplace` object. However, the `coin` parameter has type `Coin`, so use `NestedResult(0, 0)` by value. The `TxContext` input is automatically provided by the runtime. - -This gives updated memory locations, where `_` indicates the object has been moved. - -```rust -Gas Coin: Coin { id: gas_coin, ... value: 499_900u64 ... } -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, // Any mutations are applied - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [ - [ _ ], // The coin was moved - [Item { id: id1 }, Item { id: id2 }], // The results from the Move call -], -``` - -Assume that `buy_two` deletes its `Coin` object argument and transfers the `Balance` into the `Marketplace` object. - -### Command 2: `TransferObjects` - -`TransferObjects([GasCoin, NestedResult(1, 0)], Input(0))` transfers the gas coin and first item to the address at `Input(0)`. All inputs are by value, and the objects do not have `copy` so they are moved. While no results are given, the ownership of the objects is changed. This cannot be seen in the memory locations, but rather in the transaction effects. - -You now have updated memory locations of - -```rust -Gas Coin: _ // The gas coin is moved -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [ - [ _ ], - [ _ , Item { id: id2 }], // One item was moved - [], // No results from TransferObjects -], -``` - -### Command 3: `MoveCall` - -Make another Move call, this one to `iota::tx_context::sender` with the signature - -```rust -public fun sender(ctx: &TxContext): address -``` - -While you could have just passed in the sender's address as a `Pure` input, this example demonstrates calling some of the additional utility of PTBs; while this function is not an `entry` function, you can call the `public` function, too, because you can provide all of the arguments. In this case, the only argument, the `TxContext`, is provided by the runtime. The result of the function is the sender's address. Note that this value is not treated like the `Pure` inputs--the type is fixed to `address` and it cannot be deserialized into a different type, even if it has a compatible BCS representation. - -You now have updated memory locations of - -```rust -Gas Coin: _ -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [ - [ _ ], - [ _ , Item { id: id2 }], - [], - [/* senders address */ ...], // The result of the Move call -], -``` - -### Command 4: `TransferObjects` - -Finally, transfer the remaining item to yourself. This is similar to the previous `TransferObjects` command. You are using the last `Item` by-value and the sender's address by-value. The item is moved because `Item` does not have `copy`, and the address is copied because `address` does have `copy`. - -The final memory locations are - -```rust -Gas Coin: _ -Inputs: [ - Pure(/* @0x808 BCS bytes */ ...), - Marketplace { id: market_id, ... }, - Pure(/* 100u64 BCS bytes */ ...), -] -Results: [ - [ _ ], - [ _ , _ ], - [], - [/* senders address */ ...], - [], // No results from TransferObjects -], -``` - -### After commands: end of execution - -At the end of execution, the runtime checks the remaining values, which are the three inputs and -the sender's address. The following summarizes the checks performed before effects are given: - -- Any remaining input objects are marked as being returned to their original owners. - - The gas coin has been Moved. And the `Marketplace` keeps the same owner, which is shared. -- All remaining values must have `drop`. - - The Pure inputs have `drop` because any type they can instantiate has `drop`. - - The sender's address has `drop` because the primitive type `address` has `drop`. - - All other results have been moved. -- Any remaining shared objects must have been deleted or re-shared. - - The `Marketplace` object was not moved, so the owner remains as shared. - -After these checks are performed, generate the effects. - -- The coin split off from the gas coin, `new_coin`, does not appear in the effects because it was created and deleted in the same transaction. -- The gas coin and the item with `id1` are transferred to `0x808`. - - The gas coin is mutated to update its balance. The remaining gas of the maximum budget of `500_000` is returned to the gas coin even though the owner has changed. - - The `Item` with `id1` is a newly created object. -- The item with `id2` is transferred to the sender's address. - - The `Item` with `id2` is a newly created object. -- The `Marketplace` object is returned, remains shared, and it's mutated. - - The object remains shared but its contents are mutated. - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx b/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx new file mode 100644 index 00000000000..eee50e55c49 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx @@ -0,0 +1,24 @@ +--- +description: Learn about Programmable Transaction Blocks (PTBs) in IOTA and how they enhance smart contract efficiency. +--- +# Programmable Transaction Blocks Overview + +[Programmable Transaction Blocks (PTBs)](programmable-transaction-blocks) are fundamental elements within the IOTA ecosystem. Understanding how to effectively use PTBs is crucial for developing efficient and cost-effective smart contracts. + +## Building PTBs with the TypeScript SDK + +To truly harness the capabilities of PTBs, practical experience is key. Utilizing tools like the [IOTA TypeScript SDK](../../../../references/ts-sdk/typescript/index.mdx) allows you to explore the power and flexibility that PTBs offer. + +Dive deeper into this topic in [Building Programmable Transaction Blocks](building-programmable-transaction-blocks-ts-sdk). + +## Navigating Coin Management + +In IOTA, `Coin` objects are distinct because they are [owned objects](../../objects/object-ownership/address-owned.mdx), setting them apart from coins on other blockchains. Whether you're handling IOTA for gas payments or managing generic coins, a solid grasp of coin management is essential. Smart contracts typically use standard patterns to accept coins, so your PTBs must interact correctly with these contracts to facilitate successful transactions. + +Explore this subject further in [Coin Management](coin-management). + +## Simulating References with the Borrow Module + +The [`borrow` module](../../../../references/framework/iota-framework/borrow.mdx) in the IOTA framework offers valuable features for working with objects by reference within your PTBs. + +Learn how to implement these features in [Simulating References](simulating-references.mdx). diff --git a/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks.mdx b/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks.mdx new file mode 100644 index 00000000000..1c8536d0f96 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/programmable-transaction-blocks.mdx @@ -0,0 +1,485 @@ +--- +description: Learn how to execute multiple commands in a single IOTA transaction using programmable transaction blocks. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/ptb/programmable-transaction-blocks.json'; + +# Use Programmable Transaction Blocks + +Programmable transaction blocks (PTBs) let you execute multiple commands within a single IOTA transaction. +They enable you to call multiple Move functions, manage your objects, +and handle your coins—all without needing to publish a new Move package. +While PTBs are ideal for automation and transaction builders, +they don't support complex programming patterns like loops. +For such cases, you'll need to [publish a new Move package](../../../getting-started/publish.mdx). + +Each PTB is made up of individual transaction commands (also known as transactions or commands). +These commands execute sequentially, and you can use the results from one command in any subsequent command. +The effects of all commands—specifically object modifications or transfers—are applied atomically at the end of the transaction. +**If any command fails, the entire PTB fails, and none of the effects are applied.** + +Using PTBs, you can perform up to 1,024 unique operations in a single execution. +In contrast, traditional blockchains would require 1,024 individual executions to achieve the same result. +This structure not only enhances efficiency but also reduces gas fees, +as grouping transactions in a PTB is more cost-effective than processing them individually. + +This guide explains how PTBs work and how to use them effectively. +It assumes you're familiar with the IOTA [object model](../../objects/object-model.mdx) and the [Move programming language]](../../move-overview/move-overview.mdx). + +## Understanding PTB Components + +A PTB consists of two main components: + +```rust +{ + inputs: [Input], + commands: [Command], +} +``` + +- **Inputs**: A list of arguments `[Input]`, which are either objects or pure values used in the commands. +- Objects can be owned by you or be shared/immutable. Pure values are simple Move values like `u64` or `String`. + +- **Commands**: A list of commands `[Command]` that specify the actions to execute. Possible commands include: + + - [`TransferObjects`](#transferobjects): Send one or more objects to a specified address. + - [`SplitCoins`](#splitcoins): Split a single coin into multiple coins. + - [`MergeCoins`](#mergecoins): Combine multiple coins into one coin. + - [`MakeMoveVec`](#makemovevec): Create a vector of Move values, primarily used as arguments for `MoveCall`. + - [`MoveCall`](#movecall): Invoke an `entry` or `public` Move function in a published package. + - [`Publish`](#publish): Create a new package and call the `init` function of each module. + - [`Upgrade`](#upgrade): Upgrade an existing package, gated by [`iota::package::UpgradeCap`](../../../../references/framework/iota-framework/package.mdx#resource-upgradecap). + +### Inputs and Results + +**Inputs** are the values you provide to the PTB, either as objects or pure values. **Results** are the values produced by the commands within the PTB. You can access these values by borrowing, copying (if allowed), or moving them. + +#### Inputs + +Inputs are categorized as objects or pure values: + +- **Object Inputs**: Include the necessary metadata to specify the object being used. Depending on the object's ownership type, the metadata differs: + + - **Owned or Immutable Objects**: Use `ObjectArg::ImmOrOwnedObject(ObjectID, SequenceNumber, ObjectDigest)`. + - **Shared Objects**: Use `ObjectArg::SharedObject { id: ObjectID, initial_shared_version: SequenceNumber, mutable: bool }`. A shared objects version and digest are determined by the network's consensus protocol. The `mutable` flag indicates whether the object is to be used mutably in this transaction + - **Objects Owned by Other Objects**: Use `ObjectArg::Receiving(ObjectID, SequenceNumber, ObjectDigest)`. + +- **Pure Inputs**: Provide [BCS](../../../../references/framework/iota-framework/bcs.mdx) bytes deserialized into Move values. Only certain types are allowed, such as primitive types, strings, object IDs, vectors, and options. The following types are allowed to be used with pure values: + - All primitive types: + - `u8` + - `u16` + - `u32` + - `u64` + - `u128` + - `u256` + - `bool` + - `address` + - A string, either an ASCII string (`std::ascii::String`) or UTF8 string (`std::string::String`). In either case, the bytes are validated to be a valid string with the respective encoding. + - An object ID `iota::object::ID`. + - A vector, `vector`, where `T` is a valid type for a pure input, checked recursively. + - An option, `std::option::Option`, where `T` is a valid type for a pure input, checked recursively. + +#### Results + +Each command may produce a (possibly empty) array of results. The number and types of results depend on the command: + +- `MoveCall`: Returns results based on the Move function called. +- `SplitCoins`: Produces coins from a single [coin](../../../../references/framework/iota-framework/coin.mdx). +- `Publish`: Returns the [upgrade capability](../../../../references/framework/iota-framework/package.mdx#resource-upgradecap) for the new package. +- `Upgrade`: Returns the [upgrade receipt](../../../../references/framework/iota-framework/package.mdx#struct-upgradereceipt) for the upgraded package. +- `TransferObjects` and `MergeCoins`: Do not produce results. + +### Using Arguments in Commands + +Commands accept `Argument`s to specify inputs or results: + +- `Input(u16)`: Refers to an input by its index in the input list, where the `u16` is the index of the input in the input vector. + - `GasCoin`: Represents the [IOTA coin](../../../../references/framework/iota-framework/coin.mdx) used for gas payment. It cannot be taken by-value except with `TransferObjects`. To get an owned version, use `SplitCoins` to create a new coin. + + This limitation ensures that any remaining gas is returned to the gas coin at the end of execution. + If the gas coin were wrapped or deleted, there would be no place to return the excess gas. + +- `NestedResult(u16, u16)`: Uses a value from a previous command's results. The first `u16` is the command index, and the second is the result index within that command. +- `Result(u16)`: Equivalent to `NestedResult(i, 0)`, but errors if the result array is empty or has more than one value. + +## Executing Programmable Transaction Blocks + +When you execute a PTB, the process involves three main steps: + +1. **[Initializing Inputs](#1-initializing-inputs)**: The runtime loads input objects and pure value bytes into the input array. +2. **Executing Commands**: Transaction commands are executed sequentially, and results are stored. +3. **Applying Effects**: The transaction's effects are applied atomically at the end. + +Let's explore each of these steps in detail. + +### 1. Initializing Inputs + +At the start, the PTB runtime loads the input objects into the input array. +The network has already verified these objects for existence and valid ownership. +Pure value bytes are also loaded but are not validated until they are used. + +:::note Gas + +The gas coin is important at this stage. +The maximum gas budget (in IOTA tokens) is withdrawn from the gas coin at the beginning. +Any unused gas is returned to the gas coin at the end of execution, even if the coin's ownership has changed. + +::: + +### 2. Executing Commands + +Each transaction command is executed in order. +Before diving into specific commands, it's crucial to understand how arguments are used. + +#### Using Arguments + +Arguments can be used by-reference or by-value, depending on their type and the command's requirements. + +- **Mutable Reference (`&mut T`)**: The argument must be of type `T` and is mutably borrowed. +- **Immutable Reference (`&T`)**: The argument must be of type `T` and is immutably borrowed. +- **By-Value (`T`)**: The argument must be of type `T`. If `T` has the `copy` trait, it's copied; otherwise, it's moved. + +:::note + +Once you move an argument, you cannot use it again. +If you try to use an argument after it's been moved, the transaction will fail. + +::: + +When borrowing arguments: + +- **Mutable Borrow**: No other borrows (mutable or immutable) can exist to avoid references that point to invalid memory. +- **Immutable Borrow**: No mutable borrows can exist; multiple immutable borrows are allowed. +- **Moving or Copying**: There can be outstanding borrows, mutable or immutable. While it might lead to some unexpected results in some cases, there is no safety concern + +#### Special Considerations + +- **Object Inputs**: For `ObjectArg::Receiving` inputs, the object type `T` is wrapped as [`iota::transfer::Receiving`](../../../../references/framework/iota-framework/transfer.mdx#struct-receiving). You need to call [`iota::transfer::receive`](../../../../references/framework/iota-framework/transfer.mdx#function-receive) to prove ownership. +- **Gas Coin**: You can only use the gas coin by-value with the [`TransferObjects`](#transferobjects) command. This ensures that any remaining gas can be returned to it. + - **Shared Objects**: Shared objects have restrictions to ensure they remain shared or are deleted by the end of the transaction. They cannot be unshared or wrapped. + A shared object: + - Marked as not `mutable` (being used read-only) cannot be used by value. + - Cannot be transferred or frozen. These checks are done at the end of the transaction only. For example, `TransferObjects` succeeds if passed a shared object, but at the end of execution the transaction fails. + - Can be wrapped and can become a dynamic field transiently, but by the end of the transaction it must be re-shared or deleted. + + +#### Pure Values + +Pure values are not type-checked until used. +They can be used with multiple types as long as the bytes are valid for each type. +Once you mutably borrow a pure value, its type becomes fixed for subsequent uses. +For example, you can use a [string](../../../../references/framework/move-stdlib/string.mdx) as an ASCII string `std::ascii::String` and as a UTF8 string `std::string::String`. +However, after you mutably borrow the pure value, the type becomes fixed, and all future usages must be with that type. + +#### Command Execution Details + +Let's look at how specific commands are executed. + +##### `TransferObjects` + +- **Syntax**: `TransferObjects(ObjectArgs, AddressArg)` +- **Usage**: + - `ObjectArgs`: A list of objects to transfer (by-value). + - `AddressArg`: The recipient's address (by-value) from a `Pure` input or a result. +- **Notes**: + - Objects can be of different types. + - The command does not produce any results. + +##### `SplitCoins` + +- **Syntax**: `SplitCoins(CoinArg, AmountArgs)` +- **Usage**: + - `CoinArg`: The coin to split (mutable reference). Must be a coin of type [`iota::coin::Coin`](../../../../references/framework/iota-framework/coin.mdx). + - `AmountArgs`: Amounts to split off (by-value). Must be `u64` values, which could come from a `Pure` input or a result. +- **Notes**: + - Produces a list of new coins. + - Coin types must match. + +##### `MergeCoins` + +- **Syntax**: `MergeCoins(CoinArg, ToMergeArgs)` +- **Usage**: + - `CoinArg`: The target coin (mutable reference). Must be a coin of type [`iota::coin::Coin`](../../../../references/framework/iota-framework/coin.mdx). + - `ToMergeArgs`: Coins to merge into the target (by-value). +- **Notes**: + - Coin types must match. + - Does not produce any results. + +##### `MakeMoveVec` + +- **Syntax**: `MakeMoveVec(VecTypeOption, Args)` +- **Usage**: + - `VecTypeOption`: Optional type of the vector elements. + - `Args`: Elements to include in the vector (by-value). Copied if `T: copy` and moved otherwise. +- **Notes**: + - Produces a single vector result. + - Useful for constructing arguments for `MoveCall`. + +##### `MoveCall` + +- **Syntax**: `MoveCall(Package, Module, Function, TypeArgs, Args)` +- **Usage**: + - `Package`: Object ID of the package. + - `Module`: Name of the module. + - `Function`: Name of the function. + - `TypeArgs`: Type arguments for the function. + - `Args`: Arguments for the function. +- **Notes**: + - The number of results and argument usage depend on the function's signature. + +##### `Publish` + +- **Syntax**: `Publish(ModuleBytes, TransitiveDependencies)` +- **Usage**: + - `ModuleBytes`: Bytes of the modules to publish. + - `TransitiveDependencies`: Package IDs to link against. +- **Notes**: + - Calls the `init` function of each module. + - Produces an [`iota::package::UpgradeCap`](../../../../references/framework/iota-framework/package.mdx#resource-upgradecap). + +##### `Upgrade` + +- **Syntax**: `Upgrade(ModuleBytes, TransitiveDependencies, Package, UpgradeTicket)` +- **Usage**: + - Similar to `Publish`, but upgrades an existing package. + - `Package`: Object ID of the package to upgrade. + - `UpgradeTicket`: Obtained from [`iota::package::UpgradeCap`](../../../../references/framework/iota-framework/package.mdx#resource-upgradecap). +- **Notes**: + - Does not call the `init` function of each module. + - Produces an [`iota::package::UpgradeReceipt`](../../../../references/framework/iota-framework/package.mdx#struct-upgradereceipt). + +### 3. Applying Effects + +After all the commands have executed: + +- **Input Checks**: + - Immutable or read-only inputs remain unchanged. + - Mutable inputs return to their original owners. + - Pure inputs are dropped. Note that pure input values must have `copy` and `drop` since all permissible types for those values have `copy` and `drop`. + +- **Shared Objects**: + - Must be either deleted or remain shared. + - Cannot be unshared or wrapped by the end of the transaction. + +- **Result Handling**: + - Remaining results with the `drop` trait are dropped. + - Values with `copy` but not `drop` must have been moved in their last usage. + - Unused values without `drop` cause the transaction to fail. + +**Gas Refund**: Any unused gas is returned to the gas coin, regardless of ownership changes. + +Finally, the transaction's effects (created, mutated, and deleted objects) are applied atomically by the IOTA network. + + +## Usage Example + +Let's explore a practical example to understand how a programmable transaction block (PTB) executes. +While this won't cover every rule, it will illustrate the general execution flow. + +Suppose you want to purchase two items from a marketplace, each costing `100 NANOS`. You plan to keep one item and send the other item, along with the remaining coin balance, to a friend at address `0x808`. You can achieve all of this within a single PTB: + +```rust +{ + inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + Object(SharedObject { /* Marketplace shared object */ id: market_id, ... }), + Pure(/* 100u64 BCS bytes */ ...), + ] + commands: [ + SplitCoins(GasCoin, [Input(2)]), + MoveCall("some_package", "some_marketplace", "buy_two", [], [Input(1), NestedResult(0, 0)]), + TransferObjects([GasCoin, NestedResult(1, 0)], Input(0)), + MoveCall("iota", "tx_context", "sender", [], []), + TransferObjects([NestedResult(1, 1)], NestedResult(3, 0)), + ] +} +``` + +In this PTB: + +- **Inputs**: + - `Input(0)`: Your friend's address as a pure input. + - `Input(1)`: The shared marketplace object. + - `Input(2)`: The amount (`100u64`) to split from the gas coin. + +- **Commands**: + 1. **SplitCoins**: Splits `100u64` from the gas coin. + 2. **MoveCall**: Calls the marketplace's `buy_two` function with the marketplace object and the split coin. + 3. **TransferObjects**: Sends the gas coin and one item to your friend's address. + 4. **MoveCall**: Retrieves your own address using `iota::tx_context::sender`. + 5. **TransferObjects**: Sends the remaining item to your address. + +### Initial State + +Before executing the commands, the gas coin and marketplace object are loaded: + +```rust +Gas Coin: iota::coin::Coin { id: gas_coin, balance: iota::balance::Balance { value: 1_000_000u64 } } +Inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + some_package::some_marketplace::Marketplace { id: market_id, ... }, + Pure(/* 100u64 BCS bytes */ ...), +] +Results: [] +``` + +The gas coin has an initial balance of `1_000,000u64`. The maximum gas budget (e.g., `500,000`) is deducted upfront, leaving the gas coin with `500,000u64`. + +### Command Execution + +#### Command 0: SplitCoins + +```rust +SplitCoins(GasCoin, [Input(2)]) +``` + +- **Action**: Splits off `100u64` from the gas coin. +- **Result**: A new coin (`new_coin`) with a balance of `100u64`. + +Updated memory: + +```rust +Gas Coin: Coin { id: gas_coin, balance: 499,900u64 } +Results: [ +[Coin { id: new_coin, value: 100u64 }], +] +``` + +#### Command 1: MoveCall (`buy_two` function) + +```rust +MoveCall("some_package", "some_marketplace", "buy_two", [], [Input(1), NestedResult(0, 0)]) +``` + +- **Action**: Calls `buy_two` with the marketplace object and the split coin. +- **Assumed Signature**: + + ```rust + entry fun buy_two( + marketplace: &mut Marketplace, + coin: Coin, + ctx: &mut TxContext, + ): (Item, Item) + ``` + +- **Result**: Two items (`Item { id: id1 }` and `Item { id: id2 }`) are returned. + +Updated memory: + +```rust +Gas Coin: Coin { id: gas_coin, ... value: 499_900u64 ... } +Inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + Marketplace { id: market_id, ... }, // Any mutations are applied + Pure(/* 100u64 BCS bytes */ ...), +] +Results: [ + [ _ ], // The coin was moved + [Item { id: id1 }, Item { id: id2 }], // The results from the Move call +], +``` + +#### Command 2: TransferObjects (to friend) + +```rust +Gas Coin: _ // The gas coin is moved +Inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + Marketplace { id: market_id, ... }, + Pure(/* 100u64 BCS bytes */ ...), +] +Results: [ + [ _ ], + [ _ , Item { id: id2 }], // One item was moved + [], // No results from TransferObjects +], +``` + +#### Command 3: MoveCall (`sender` function) + +```rust +MoveCall("iota", "tx_context", "sender", [], []) +``` + +```rust +public fun sender(ctx: &TxContext): address +``` + +- **Action**: Retrieves your own address. +- **Result**: Your address is returned. + +Updated memory: + +```rust +Gas Coin: _ +Inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + Marketplace { id: market_id, ... }, + Pure(/* 100u64 BCS bytes */ ...), +] +Results: [ + [ _ ], + [ _ , Item { id: id2 }], + [], + [/* senders address */ ...], // The result of the Move call +], +``` + +#### Command 4: TransferObjects (to self) + +```rust +TransferObjects([NestedResult(1, 1)], NestedResult(3, 0)) +``` + +- **Action**: Transfers the second item to your address. +- **Result**: Ownership of `Item { id: id2 }` is transferred to you. + +Updated memory: + +```rust +Gas Coin: _ +Inputs: [ + Pure(/* @0x808 BCS bytes */ ...), + Marketplace { id: market_id, ... }, + Pure(/* 100u64 BCS bytes */ ...), +] +Results: [ + [ _ ], + [ _ , _ ], + [], + [/* senders address */ ...], + [], // No results from TransferObjects +], +``` + +### Final State and Validation + +At the end of execution, the runtime performs checks: + +- **Inputs**: + - Pure inputs are dropped (have `drop` ability). + - The marketplace object remains shared and is returned. + +- **Results**: + - All moved objects are accounted for. + - The sender's address is dropped (has `drop` ability). + +- **Gas Refund**: + - Unused gas is returned to the gas coin, even though it has changed ownership. + +### Transaction Effects + +The transaction produces the following effects: + +- **Created Objects**: + - `Item { id: id1 }` and `Item { id: id2 }` are new items. + +- **Transferred Objects**: + - Gas coin and `Item { id: id1 }` are transferred to `0x808`. + - `Item { id: id2 }` is transferred to your address. + +- **Mutated Objects**: + - The gas coin's balance is updated. + - The marketplace object is mutated but remains shared. + + diff --git a/docs/content/developer/iota-101/transactions/ptb/simulating-references.mdx b/docs/content/developer/iota-101/transactions/ptb/simulating-references.mdx new file mode 100644 index 00000000000..cfc759ec3ea --- /dev/null +++ b/docs/content/developer/iota-101/transactions/ptb/simulating-references.mdx @@ -0,0 +1,153 @@ +--- +description: Discover how to simulate references in IOTA's programmable transaction blocks using the borrow module. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/ptb/simulating-references.json'; + +# Simulating References in IOTA PTBs + +In IOTA, all on-chain data is represented as [objects](../../objects/object-model.mdx). +When developing Move packages for the IOTA network, you often need to manipulate these objects using the IOTA API. +Typically, the API functions require you to provide an object by reference. + +References are fundamental in Move programming and IOTA development. +Most IOTA API functionalities accept objects as references, which enhances security and asset safety in smart contracts. + +However, there are two primary ways to interact with an object: + +- **By Value:** You have complete ownership of the object, allowing you to destroy it, wrap it (if it has the `store` ability), or transfer it to another address. +- **By Reference:** You access the object's data without owning it, limiting operations to those defined by the module that provides the object. This method prevents you from destroying or transferring the object. References come in two forms: + - **Mutable Reference (`&mut`):** Allows you to modify the object according to the API but not destroy or transfer it. + - **Immutable Reference (`&`):** Provides read-only access to the object's data, further restricting operations. + +Currently, [programmable transaction blocks (PTBs)](programmable-transaction-blocks) in IOTA do not support +using object references returned from transaction commands. +While you can use input objects, objects created within the PTB, or objects returned by value, +you cannot use a reference returned by a transaction command in subsequent calls. +This limitation hinders certain common patterns in Move. + +## Solving with the Borrow Module + +The IOTA framework offers a [borrow module](../../../../references/framework/iota-framework/borrow.mdx) to address this reference limitation. +This module allows you to access an object by value while preventing it from being destroyed, transferred, or wrapped. +It introduces a `Referent` object that wraps the target object you wish to reference. +Using the [hot potato pattern](../../move-overview/patterns/hot-potato.mdx) through a `Borrow` instance, you can retrieve the wrapped object by value and ensure it is returned to the `Referent` within the same PTB. +The `Borrow` instance ensures the returned object is the same as the one retrieved. + +### Example Usage + +Consider a module `a_module` that defines an `Asset` object and a function `use_asset`: + +```rust +module a_module { + struct Asset has key, store { + … // some data + } + + public fun use_asset(asset: &Asset) { + …. // some code + } +} +``` + +The `use_asset` function accepts an immutable reference to `Asset`, which is common in API definitions. + +Now, imagine another module `another_module` that utilizes this asset: + +```rust +module another_module { + struct AssetManager has key { + asset: Asset, + } + + public fun get_asset(manager: &AssetManager): &Asset { + &manager.asset + } +} +``` + +Here, `AssetManager` holds a reference to `Asset` from `a_module`. +You might write a function to retrieve the asset by reference and pass it to `use_asset`: + +```rust +fun do_something(manager: &AssetManager) { + let asset = another_module::get_asset(manager); + a_module::use_asset(asset); +} +``` + +However, in PTBs, this pattern is invalid because you cannot use references returned by functions in subsequent calls. To work around this, you can modify `another_module` to use the borrow module: + +```rust +module another_module { + struct AssetManager has key { + asset: Referent, + } + + public fun get_asset(manager: &mut AssetManager): (Asset, Borrow) { + borrow::borrow(&mut manager.asset) + } + + + public fun return_asset( + manager: &mut AssetManager, + asset: Asset, + b: Borrow) { + borrow::put_back(&mut manager.asset, asset, b) + } +} +``` + +With these changes, you can retrieve the asset, use it, and then return it within the PTB. + +## Important Considerations + +The `Borrow` object is crucial for maintaining the integrity of the borrow module's guarantees. +Defined as `struct Borrow { ref: address, obj: ID }`, it cannot be dropped or stored elsewhere, +ensuring it is consumed within the same transaction (the hot potato pattern). +This structure ensures you cannot keep the retrieved object or swap it with another, maintaining consistency. + +:::caution + +Using `Referent` requires explicit changes to your codebase and can be intrusive. +Carefully consider this approach when designing your solution. + +::: + +While support for references in PTBs is forthcoming, using the borrow module is a temporary workaround. +Be mindful of the implications and plan for a transition to a more natural reference pattern in the future. + +Additionally, the `Referent` model necessitates the use of mutable references and returns objects by value, +which can significantly impact API design. +Exercise caution in how you expose objects and logic in your modules. + +## Practical Example + +Expanding on the earlier example, here's how you might write a PTB that calls `use_asset`: + +```rust +// Initialize the PTB +const txb = new TransactionBlock(); +// Load the assetManager +const assetManager = txb.object(assetManagerId); +// Retrieve the asset +const [asset, borrow] = txb.moveCall({ + target: "0xaddr1::another_module::get_asset", + arguments: [ assetManager ], +}); + +// Use the asset +txb.moveCall({ + target: "0xaddr2::a_module::use_asset", + arguments: [ asset ], +}); + +// Return the asset +txb.moveCall({ + target: "0xaddr1::another_module::return_asset", + arguments: [ assetManager, asset, borrow ], +}); +... +``` + + diff --git a/docs/content/developer/iota-101/transactions/ptb/simulating-refs.mdx b/docs/content/developer/iota-101/transactions/ptb/simulating-refs.mdx deleted file mode 100644 index 01c45da0475..00000000000 --- a/docs/content/developer/iota-101/transactions/ptb/simulating-refs.mdx +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: Simulating References -description: Use the borrow module in the IOTA framework to include objects by reference in your programmable transaction blocks. ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/ptb/simulating-refs.json'; - -Everything on the IOTA blockchain is an object. When you develop Move packages for the IOTA network, you're typically manipulating or using on-chain objects in some way through functionality available in the IOTA API. For most API functions, you provide an object by reference. - -References are a key construct when programming in Move and on IOTA. Most of the functionality available in the IOTA API takes objects by reference. - -There are two ways to use an object: -- **by value:** When you use an object by value, you have full control over that object. You can destroy it (if the functionality is available), wrap it (if it has the `store` ability), or transfer it to an address. -- **by reference:** When you use an object by reference, operations over that object are determined by the logic the module that defines the object provides because you are using a reference to its data rather than having ownership of the object itself. The restrictions of references allow you to develop smart contracts with a high level of security and safety around assets. There are two types of references: - - Mutable reference (`&mut`): You can alter the object (according to the API) but you can't destroy or transfer it. - - Immutable reference (`&`): Further restricts the set of operations and the guarantees/invariants over the referenced object. You have read-only access to the object's data. - -Programmable transaction blocks (PTBs) do not currently allow the use of object references returned from one of its transaction commands. You can use input objects to the PTB, objects created by the PTB (like `MakeMoveVec`), or returned from a transaction command by value as references in subsequent transaction commands. If a transaction command returns a reference, however, you can't use that reference in any call, significantly limiting certain common patterns in Move. - -## The borrow module - -The IOTA framework includes a [borrow](https://github.com/iotaledger/iota/blob/main/crates/iota-framework/docs/borrow.md) module that offers a solution to the reference problem. The module provides access to an object by value but builds a model that makes it impossible to destroy, transfer, or wrap the object retrieved. The borrow module exposes a `Referent` object that wraps another object (the object you want to reference). The module uses the [hot potato pattern](../../move-overview/patterns/hot-potato.mdx) (via a `Borrow` instance) to allow retrieval of the wrapped object by value. Within the same PTB, the module then forces the object to be returned to the `Referent`. The `Borrow` instance guarantees that the object returned is the same that was retrieved. - -As an example, consider the following module stub that exposes an object (`Asset`) and a function (`use_asset`) to use that object. - -```rust -module a_module { - struct Asset has key, store { - … // some data - } - - public fun use_asset(asset: &Asset) { - …. // some code - } -} -``` - -The function `use_asset` takes an immutable reference to the asset (`&Asset`), which is a common pattern in a an API definition. - -Now consider another module that uses this asset. - -```rust -module another_module { - struct AssetManager has key { - asset: Asset, - } - - public fun get_asset(manager: &AssetManager): &Asset { - &manager.asset - } -} -``` - -This module creates an object (`AssetManager`) that references the object (`Asset`) created in the previous module (`a_module`). - -You could then write a Move function that retrieves an object by reference and passes it to the `use_asset` function. - -```rust -fun do_something(manager: &AssetManager) { - let asset = another_module::get_asset(manager); - a_module::use_asset(asset); -} -``` - -The two functions in `do_something` are not valid within a PTB, however, because PTBs do not support a reference returned by a function and passed to another function. - -To make this operation valid within a PTB, you would need to include functionality from the borrow module. Consequently, you could change the `another_module` code to the following: - -```rust -module another_module { - struct AssetManager has key { - asset: Referent, - } - - public fun get_asset(manager: &mut AssetManager): (Asset, Borrow) { - borrow::borrow(&mut manager.asset) - } - - - public fun return_asset( - manager: &mut AssetManager, - asset: Asset, - b: Borrow) { - borrow::put_back(&mut manager.asset, asset, b) - } -} -``` - -Now the PTB can retrieve the asset, use it in a call to `use_asset`, and return the asset. - -## Considerations - -The `Borrow` object is the key to the guarantees the borrow module offers. The definition of `Borrow` is -`struct Borrow { ref: address, obj: ID }` -which makes it such that you cannot drop or save its instance anywhere, so it must be consumed in the same transaction that retrieves it (hot potato). Moreover, fields in the `Borrow` struct make sure that the object returned is for the same `Referent` and the object that was originally held by the `Referent` instance. In other words, there is no way to either keep the object retrieved or to swap it with another object in a different `Referent`. - -:::caution - -Using a `Referent` is a very explicit and intrusive change. That has to be taken into consideration when designing a solution. - -::: - -Support for references in a PTB is planned, which is a much more natural and proper pattern for APIs. - -You must consider the implications of using the borrow module and whether you have a mechanism to later move to a more natural, reference pattern. - -Finally, the `Referent` model forces the usage of a mutable reference and returns an object by value. Both have significant implications when designing an API. You must be careful in what logic your modules provide and how objects are exposed. - -## Example - -Extending the previous example, a PTB that calls `use_asset` is written as follows: - -```rust -// initialize the PTB -const txb = new TransactionBlock(); -// load the assetManager -const assetManager = txb.object(assetManagerId); -// retrieve the asset -const [asset, borrow] = txb.moveCall({ - target: "0xaddr1::another_module::get_asset", - arguments: [ assetManager ], -}); -// use the asset -txb.moveCall({ - target: "0xaddr2::a_module::use_asset", - arguments: [ asset ], -}); -// return the asset -txb.moveCall({ - target: "0xaddr1::another_module::return_asset", - arguments: [ assetManager, asset, borrow ], -}); -... -``` - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/ptb/working-with-ptbs.mdx b/docs/content/developer/iota-101/transactions/ptb/working-with-ptbs.mdx deleted file mode 100644 index ea045d4c2e8..00000000000 --- a/docs/content/developer/iota-101/transactions/ptb/working-with-ptbs.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Working with Programmable Transaction Blocks ---- - -Programmable transaction blocks (PTBs) are key elements of the IOTA ecosystem. Understanding PTBs and using them correctly are key fundamentals to creating efficient and cost-effective smart contracts. See [Programmable Transaction Blocks](prog-txn-blocks.mdx) to learn about the structure of PTBs on IOTA. - -The topics in this section focus on effectively utilizing PTBs in your smart contracts. - -## Building Programmable Transaction Blocks - -To fully appreciate the possibilities PTBs offer, you must build them. Using tools like the [IOTA TypeScript SDK](../../../../references/ts-sdk/typescript/index.mdx), you can begin to understand the power and flexibility they provide. - -Go to [Building Programmable Transaction Blocks](building-ptb.mdx). - -## Coin Management - -`Coin` objects on IOTA are different than other blockchains in that they are [owned objects](../../objects/object-ownership/address-owned.mdx). Whether you need your smart contract to utilize IOTA for gas payments or deal with generic coins, understanding coin management is crucial. Smart contracts use common patterns to accept coins and the PTBs you create must provide the correct interface to those smart contracts to facilitate successful transactions. - -Go to [Coin Management](coin-mgt.mdx). - -## Simulating References - -The [`borrow` module](/references/framework/iota-framework/borrow.mdx) of the IOTA framework offers some features you can use when your PTBs use objects by reference. - -Go to [Simulating References](simulating-refs.mdx). - -## Related links - -Review this content for a complete picture of PTBs on IOTA. - -- [Programmable Transaction Blocks](prog-txn-blocks.mdx): Conceptual overview of the PTB architecture. -- [Life of a Transaction](../../../../about-iota/iota-architecture/transaction-lifecycle.mdx): Discover the life of a transaction from inception to finality. diff --git a/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx b/docs/content/developer/iota-101/transactions/sign-and-send-transactions.mdx similarity index 61% rename from docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx rename to docs/content/developer/iota-101/transactions/sign-and-send-transactions.mdx index d71187d39a0..f94e360b26e 100644 --- a/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx +++ b/docs/content/developer/iota-101/transactions/sign-and-send-transactions.mdx @@ -1,48 +1,62 @@ --- -title: Signing and Sending Transactions +description: A guide on how to construct, sign, and submit transactions in a Move-based blockchain. --- -import AlphaNet from "../../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/sign-and-send-txn.json'; +import questions from '/json/developer/iota-101/transactions/sign-and-send-transactions.json'; -Transactions in IOTA represent calls to specific functionality (like calling a smart contract function) that execute on inputs to define the result of the transaction. +# Signing and Submitting Transactions -Inputs can either be an object reference (either to an owned object, an immutable object, or a shared object), or an encoded value (for example, a vector of bytes used as an argument to a Move call). After a transaction is constructed, usually through using [programmable transaction blocks](ptb/building-ptb.mdx) (PTBs), the user signs the transaction and submits it to be executed on chain. -The signature is provided with the private key owned by the wallet, and its public key must be consistent with the transaction sender's IOTA address. +In Move-based blockchains, transactions are the primary means to interact with the network, such as invoking smart contract functions or transferring assets. -IOTA uses a `IotaKeyPair` to produce the signature, which commits to the Blake2b hash digest of the intent message (`intent || bcs bytes of tx_data`). The signature schemes currently supported are `Ed25519 Pure`, `ECDSA Secp256k1`, `ECDSA Secp256r1` and `Multisig`. +## Understanding Transactions in Move -You can instantiate `Ed25519 Pure`, `ECDSA Secp256k1`, and `ECDSA Secp256r1` using `IotaKeyPair` and use it to sign transactions. Note that this guide does not apply to `Multisig`, please refer to it's [own page](../../cryptography/transaction-auth/multisig.mdx) for instructions. +Transactions in Move represent function calls or operations that affect the state of the blockchain. They are executed based on provided inputs, which can be: -With a signature and the transaction bytes, a transaction can be submitted to be executed. +- **Object References**: These refer to [owned objects](../objects/object-ownership/address-owned.mdx), [immutable objects](../objects/object-ownership/immutable.mdx), or [shared objects](../objects/object-ownership/shared.mdx) within the blockchain state. +- **Encoded Values**: For example, a [vector](../../../references/framework/move-stdlib/vector.mdx) of bytes used as arguments in a Move function call. -## Workflow +After constructing a transaction—typically using [Programmable Transaction Blocks](ptb/building-programmable-transaction-blocks-ts-sdk.mdx) (PTBs)—you need to sign it and submit it to the network. -The following high-level process describes the overall workflow for constructing, signing and executing an on-chain transaction: +The signature must be generated using your private key, and the corresponding public key must match the sender's blockchain address. -- Construct the transaction data by creating a `Transaction` where multiple transactions are chained. See [Building Programmable Transaction Blocks](ptb/building-ptb.mdx) for more information. -- The [SDK's](../../../references/iota-sdks.mdx) built-in gas estimation and coin selection picks the gas coin. -- Sign the transaction to generate a [signature](../../cryptography/transaction-auth/signatures.mdx). -- Submit the `Transaction` and its signature for on-chain execution. +IOTA uses a `IotaKeyPair` for signature generation, committing to the Blake2b hash of the intent message (`intent || serialized transaction data`). Supported signature schemes include `Ed25519`, `ECDSA Secp256k1`, `ECDSA Secp256r1`, and `Multisig`. + +You can instantiate key pairs for `Ed25519`, `ECDSA Secp256k1`, and `ECDSA Secp256r1` using `IotaKeyPair` and use them to sign transactions. + +:::info Multisig + +For `Multisig`, please refer to its [dedicated guide](../../cryptography/transaction-auth/multisig.mdx). + +::: + +Once you have the signature and the transaction bytes, you can submit the transaction for execution on-chain. + +## Workflow Overview + +Here is a high-level overview of the steps involved in constructing, signing, and executing a transaction: + +1. **Construct the Transaction Data**: Create a `Transaction` where you can chain multiple operations. Refer to [Creating Programmable Transaction Blocks](ptb/building-programmable-transaction-blocks-ts-sdk.mdx) for guidance. +2. **Select Gas Coin and Estimate Gas**: Use the [SDK's](../../../references/iota-sdks.mdx) built-in tools for gas estimation and coin selection. +3. **Sign the Transaction**: Generate a [signature](../../cryptography/transaction-auth/signatures.mdx) using your private key. +4. **Submit the Transaction**: Send the `Transaction` and its signature to the network for execution. :::info -If you want to use a specific gas coin, first find the gas coin object ID to be used to pay for gas, and explicitly use that in the PTB. If there is no gas coin object, use the [splitCoin](ptb/building-ptb.mdx#available-transactions) transaction to create a gas coin object. The split coin transaction should be the first transaction call in the PTB. +If you wish to specify a particular gas coin, locate the gas coin object ID that you own and include it explicitly in the PTB. If you don't have a gas coin object, use the [splitCoin](ptb/building-programmable-transaction-blocks-ts-sdk.mdx#txbsplitcoinscoin-amounts) operation to create one. Ensure that the split coin transaction is the first call in the PTB. ::: ## Examples - -The following examples demonstrate how to sign and execute transactions using Rust, TypeScript, or the IOTA CLI. +Below are examples demonstrating how to sign and submit transactions using Rust, TypeScript, or the Move CLI. - + -There are various ways to instantiate a key pair and to derive its public key and IOTA address using the [IOTA TypeScript SDK](../../../references/ts-sdk/typescript/index.mdx). +There are several methods to create a key pair and derive its public key and Move address using the [Move TypeScript SDK](../../../references/ts-sdk/typescript/index.mdx). ```tsx import { fromHEX } from '@iota/bcs'; @@ -58,13 +72,13 @@ const kp_rand_1 = new Secp256k1Keypair(); const kp_rand_2 = new Secp256r1Keypair(); const kp_import_0 = Ed25519Keypair.fromSecretKey( - fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), +fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), ); const kp_import_1 = Secp256k1Keypair.fromSecretKey( - fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), +fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), ); const kp_import_2 = Secp256r1Keypair.fromSecretKey( - fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), +fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'), ); // $MNEMONICS refers to 12/15/18/21/24 words from the wordlist, e.g. "retire skin goose will hurry this field stadium drastic label husband venture cruel toe wire". Refer to [Keys and Addresses](../cryptography/transaction-auth/keys-addresses.mdx) for more. @@ -108,9 +122,9 @@ console.log(res); -The full code example below can be found under [crates/iota-sdk](https://github.com/iotaledger/iota/blob/develop/crates/iota-sdk/examples/sign_tx_guide.rs). +The complete code example is available in the [iota-sdk](https://github.com/move-language/move/blob/main/language/move-sdk/examples/sign_tx_guide.rs). -There are various ways to instantiate a `IotaKeyPair` and to derive its public key and IOTA address using the [IOTA Rust SDK](../../../references/rust-sdk.mdx). +You can create a `IotaKeyPair` and derive its public key and IOTA address using the [IOTA Rust SDK](../../../references/rust-sdk.mdx). ```rust // deterministically generate a keypair, testing only, do not use for mainnet, use the next section to randomly generate a keypair instead. @@ -134,7 +148,7 @@ There are various ways to instantiate a `IotaKeyPair` and to derive its public k let _ikp_import_no_flag_1 = IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes( &Base64::decode("1GPhHHkVlF6GrCty2IuBkM+tj/e0jn64ksJ1pc8KPoI=") .map_err(|_| anyhow!("Invalid base64"))?, - )?); +)?); let _ikp_import_no_flag_2 = IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes( &Base64::decode("1GPhHHkVlF6GrCty2IuBkM+tj/e0jn64ksJ1pc8KPoI=") .map_err(|_| anyhow!("Invalid base64"))?, @@ -156,27 +170,27 @@ There are various ways to instantiate a `IotaKeyPair` and to derive its public k let sender = IOTAAddress::from(&pk); ``` -Next, sign transaction data constructed using an example programmable transaction block with default gas coin, gas budget, and gas price. See [Building Programmable Transaction Blocks](ptb/building-ptb.mdx) for more information. +Next, sign transaction data constructed using an example programmable transaction block with default gas coin, gas budget, and gas price. See [Building Programmable Transaction Blocks](ptb/building-programmable-transaction-blocks-ts-sdk.mdx) for more information. ```rust // construct an example programmable transaction. let pt = { - let mut builder = ProgrammableTransactionBuilder::new(); +let mut builder = ProgrammableTransactionBuilder::new(); builder.pay_iota(vec![sender], vec![1])?; - builder.finish() - }; +builder.finish() +}; - let gas_budget = 5_000_000; +let gas_budget = 5_000_000; let gas_price = iota_client.read_api().get_reference_gas_price().await?; // create the transaction data that will be sent to the network. - let tx_data = TransactionData::new_programmable( +let tx_data = TransactionData::new_programmable( sender, - vec![gas_coin.object_ref()], +vec![gas_coin.object_ref()], pt, - gas_budget, - gas_price, - ); +gas_budget, +gas_price, +); ``` Commit a signature to the Blake2b hash digest of the intent message (`intent || bcs bytes of tx_data`). @@ -205,26 +219,26 @@ Finally, submit the transaction with the signature. ```rust let transaction_response = iota_client - .quorum_driver_api() - .execute_transaction_block( +.quorum_driver_api() +.execute_transaction_block( iota_types::transaction::Transaction::from_generic_sig_data( - intent_msg.value, +intent_msg.value, Intent::iota_transaction(), vec![GenericSignature::Signature(iota_sig)], - ), +), IOTATransactionResponseOptions::default(), - None, - ) - .await?; +None, +) +.await?; ``` -When using the [IOTA CLI](../../../references/cli.mdx) for the first time, it creates a local file in `~/.iota/keystore` on your machine with a list of private keys (encoded as Base64 encoded `flag || 32-byte-private-key`). You can use any key to sign transactions by specifying its address. Use `iota keytool list` to see a list of addresses. +When you first use the [IOTA CLI](../../../references/cli.mdx), it creates a keystore at `~/.move/keystore` on your machine, storing your private keys. You can sign transactions using any key by specifying its address. Use `move keytool list` to view your addresses. -There are three ways to initialize a key: +You can initialize a key in three ways: ```shell # generate randomly. @@ -257,11 +271,11 @@ iota client execute-signed-tx --tx-bytes $TX_BYTES --signatures $SERIALIZED_SIGN -### Notes +### Additional Notes -1. This guide demonstrates how to sign with a single private key. Refer to [Multisig](../../cryptography/transaction-auth/multisig.mdx) when it is preferred to set up more complex signing policies. -2. If you decide to implement your own signing mechanisms instead of using the previous tools, see the [Signatures](../../cryptography/transaction-auth/signatures.mdx) doc on the accepted signature specifications for each scheme. -3. Flag is one byte that differentiates signature schemes. See supported schemes and its flag in [Signatures](../../cryptography/transaction-auth/signatures.mdx). -4. The `execute_transaction_block` endpoint takes a list of signatures, so it should contain exactly one user signature, unless you are using sponsored transaction that a second signature for the gas object can be provided. See [Sponsored Transactions](sponsored-transactions.mdx) for more information. +1. This tutorial focuses on signing with a single private key. For more complex signing policies, refer to the [Multisig](../../cryptography/transaction-auth/multisig.mdx) guide. +2. If you implement your own signing mechanism, consult the [Signatures](../../cryptography/transaction-auth/signatures.mdx) documentation for accepted specifications. +3. The 'flag' is a byte that differentiates signature schemes. See [Signatures](../../cryptography/transaction-auth/signatures.mdx) for supported schemes and their flags. +4. The `execute_transaction_block` endpoint accepts a list of signatures, typically requiring only one user signature unless using sponsored transactions. For more information, see [Sponsored Transactions](sponsored-transactions/about-sponsored-transactions.mdx). - \ No newline at end of file + diff --git a/docs/content/developer/iota-101/transactions/sponsor-txn.mdx b/docs/content/developer/iota-101/transactions/sponsor-txn.mdx deleted file mode 100644 index dcbf2cf3d54..00000000000 --- a/docs/content/developer/iota-101/transactions/sponsor-txn.mdx +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: Sponsored Transaction ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/sponsor-txn.json'; - -Sponsored transactions are a primitive on the IOTA blockchain that enable the execution of a transaction without a user paying the gas. It also discusses the roles in Sponsored Transaction, and a few common use cases. Then it discusses the flow of Sponsored Transaction, mostly for developers who are interested in building a Gas Station or integrate with one. Finally it talks about risk considerations of Sponsored Transaction. - -# Overview -A transaction on IOTA takes a payment to execute. The payment, also known as gas, is a list of `0x2::coin::Coin<0x2::iota::IOTA>` objects, and paid to IOTA validators to secure the network. Although gas is a critical piece in [IOTA tokenomics](../../../about-iota/tokenomics/tokenomics.mdx), it sometimes adds challenges when new users start to navigate on IOTA, especially for web 2.0 users. - -Sponsored transactions can reduce the onboarding friction for users because the feature streamlines the process for end users. Using sponsored transactions, you can execute a transaction without requiring the user to pay it themselves. Instead, you can act as a sponsor of the transaction, offering your own payment gas objects for the transaction. - -## Roles in sponsored transactions - -In a sponsored transaction there are three roles: the user, the gas station, and the sponsor. - -* The user is the entity who wants to execute a transaction. -* The gas station is the entity that fulfills the sponsorship request for the user's transaction by providing the gas payment they own. -* The sponsor is entity that funds the gas station for its operations. - -It's not uncommon for the gas station and the sponsor to be the same entity. For example, a web3 gaming studio could run its own gas station to sponsor users with real free-to-play gaming experiences at its user acquisition stage. Because it's not always trivial to maintain a gas station for teams of any size, that gaming studio could also leverage third-party gas stations to sponsor transactions they want to promote. - -The remainder of this guide assumes the sponsor uses their own gas station. - -## Use cases - -The following sections describe some common scenarios where sponsored transactions offer an improved user experience. - -### App-specific sponsorship - -In this scenario, the sponsor has a specific set of applications they want to sponsor. - -- If the transaction is initialized by the user, the sponsor examines the transaction to make sure it's within the set of approved applications before agreeing to provide the gas payment. -- If the transaction is proposed by the sponsor, the user must examine the transaction and decide if they want to execute it. Examples of this type of transaction might include a rewards claim transaction of a campaign or a "try it out" advertisement transaction. - -### Wildcard sponsorship - -In this scenario, the sponsor has few restrictions on the type of transactions the gas payment can be used for. - -- If the sponsor is a gasless wallet, it may agree to sponsor any valid transactions proposed by its users. -- In the form of a reward or discount, the sponsor could offer the user a wildcard gas payment, expressly promising to execute any transactions with that gas payment. - -A sponsored transaction is not restricted to these use cases. Essentially, a sponsored transaction is any transaction jointly made by the user and the sponsor. As long as the stakeholders can agree on the transaction details, then the number of possible ways to provide sponsored transactions is limited only by the imagination. Because at least two stakeholders are involved in a sponsored transaction, however, there are some [additional risks](#risk} that you should take steps to mitigate. - - -## Sponsored transaction flow - -This section is mostly for developers who are interested in building a gas station or integrating with one. - -The data structure of a transaction resembles the following: - -```rust - -pub struct SenderSignedTransaction { - pub intent_message: IntentMessage, - /// A list of signatures signed by all transaction participants. - /// 1. non participant signature must not be present. - /// 2. signature order does not matter. - pub tx_signatures: Vec, -} - -pub struct TransactionDataV1 { // <-- A variant of `TransactionData` - pub kind: TransactionKind, // <-- This is the actual transaction details - pub sender: IOTAAddress, - pub gas_data: GasData, - pub expiration: TransactionExpiration, -} - -pub struct GasData { - pub payment: Vec, - pub owner: IOTAAddress, - pub price: u64, - pub budget: u64, -} - -``` - -A few details of note for the preceding code: - -- `sender` in `TransactionDataV1` (a variant of `TransactionData`) is the user address. -- `gas_data` in `TransactionDataV1` is the gas payment. -- `GasData` allows a list of gas objects, but the same address must own them, namely the `owner` in `GasData` (the sponsor). When `owner` is equal to `sender`, then it is a regular/non-sponsored transaction. -- `tx_signatures` in `SenderSignedTransaction` is a list of signatures. For a sponsored transaction, the list needs to contain both signatures of the user and the sponsor in some order. The signatures are signed over the entire `TransactionData`, including `GasData`. - -So, to construct a correct sponsored transaction, you must first build a `TransactionData` object. If you are neither the user or the sponsor, you would then pass the transaction to both parties to sign. If you're the sponsor, you would sign the transaction and then pass it and the signature to the other party (in the form of `SenderSignedTransaction`) for them to sign. In practice, the latter is the more common scenario. - -There are three flows of sponsored transaction. - -**User proposed transaction** - -([swimlane link](https://swimlanes.io/d/wAcnOpA_h)) - -![](https://static.swimlanes.io/b090340af36c8a4af6c36d4479a4d04f.png) - -**Sponsor proposed transaction** - -([swimlane link](https://swimlanes.io/#ZZE9T8QwDIb3/ApvLIWBsQMSEh8DEkI6mInb+O6itk4VOxInxH8nqe76AVki+X392G+iXnuq4fYGdmNgCRHeYhiDkIP3iCzYqg9syuGg2WmstWZMDYjG1Ora9YCK8G0gn2LoPLt6rb/kQjXLQuwo1rBL/t65SCKLdkD5dJlWwzNKwS4SfY0+YsFt2I9zuTI/047mEuj67pKthjmlHj13ki+CD6EIA56gIfCslFfRHN8zIOgyoQJkB2G/pyhZsefNLKDAiKeBWDczC7YG++d97EQ5264ExB8YNUUyZtpjbmyP1Hbb+cX7H7g0PqW+fw0uf5KkZvAKLmE/deU4K9Iv)) - -![](https://static.swimlanes.io/d917884a263c494bb6127102d0f64840.png) - - -**Wildcard gas payment** - - -([swimlane link](https://static.swimlanes.io/ee3962b3ac3cc5d34f317cecdde125b0.png)) - -![](https://static.swimlanes.io/ee3962b3ac3cc5d34f317cecdde125b0.png) - - -## Risk considerations {#risk} - -Because at least two stakeholders are involved in a sponsored transaction, you should take steps to mitigate risk. - -### Client equivocation risk - -Client equivocation happens when more than one legit transaction that shares at least one owned object (such as a gas coin object) at a certain version are submitted to the network simultaneously. On IOTA, before a transaction is executed, owned objects in this transaction are locked on validators at specific versions. An honest validator only accepts one transaction and rejects others. Depending on the order validators receive these transactions, validators might accept different transactions. In the event of no single transaction getting accepted by at least 2/3rds of validators, the owned object is locked until end of the epoch. - -Practically speaking, client equivocation is rare, mostly caused by buggy client software. After all, no one has incentives to lock their own objects. However, sponsored transactions come with counterparty risks. For example, a malicious user could equivocate the gas station's gas coin object by submitting another transaction that uses one owned object in the gas station signed transaction at the same version. Similarly, a Byzantine gas station could do the same to the user owned objects. - -Although this risk might seem trivial, it is helpful to be aware of it. Your gas station should actively monitor user behavior and alert on anything abnormal. Whether you're a user taking advantage of sponsored transactions or a developer integrating with a gas station, consider your reputation to minimize the risk of client equivocation. - -Both the user and the sponsor need to sign over the entire `TransactionData`, including `GasData` because otherwise a third party (such as a malicious Full node) could snip the partially signed data and cause client equivocation and locking of owned objects. - -### Censorship risk - -If you chooses to submit the dual-signed transaction to the sponsor or gas station rather than a Full node, the transaction might be subject to sponsor or gas station censorship. Namely, the sponsor might choose not to submit the transaction to the network, or delay the submission. - -You can mitigate this risk by submitting the transaction directly to a Full node. - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/sponsored-transactions.mdx b/docs/content/developer/iota-101/transactions/sponsored-transactions.mdx deleted file mode 100644 index 3aaea983603..00000000000 --- a/docs/content/developer/iota-101/transactions/sponsored-transactions.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: Sponsored Transactions ---- -import Quiz from '@site/src/components/Quiz'; -import questions from '/json/developer/iota-101/transactions/sponsored-transactions.json'; - -An IOTA sponsored transaction is one where an IOTA address (the sponsor's) pays the gas fees for a transaction that another address (the user's) initializes. You can use sponsored transactions to cover the fees for users on your site or app so that they don't get charged for them. This removes a significant obstacle that web 2.0 users encounter when entering web3, as they often have to purchase tokens to perform a transaction on chain. For example, you could sponsor gamers' early transactions to increase conversion rates. - -Sponsored transactions also facilitate asset management as you don't need to maintain multiple accounts with IOTA tokens to transfer funds. - -You can use IOTA sponsored transactions to: -- Sponsor (pay gas fees for) a transaction a user initiates. -- Sponsor transactions you initiate as the sponsor. -- Provide a wildcard `GasData` object to users. The object covers the gas fees for a user transaction. The `GasData` object covers any fee amount determined for the transaction as long as the budget is sufficient. - -## Potential risks using sponsored transactions - -The most significant potential risk when using sponsored transactions is [equivocation](/references/iota-glossary.mdx#equivocation). In some cases under certain conditions, a sponsored transaction can result in all associated owned objects, including gas in a locked state when examined by IOTA validators. To avoid double spending, validators lock objects as they validate transactions. An equivocation occurs when an owned object's pair (`ObjectID`, `SequenceNumber`) is concurrently used in multiple non-finalized transactions. - -To equivocate, either the user or the sponsor signs and submits another transaction that attempts to manipulate an owned object in the original transaction. Because only the object owner can use an owned object, only the user and sponsor can cause this condition. - -## Create a user-initiated sponsored transaction - -A user-initiated sponsored transaction involves the following steps: - - 1. A user initializes a `GasLessTransactionData` transaction. - 1. The user sends `GasLessTransactionData` to the sponsor. - 1. The sponsor validates the transaction, constructs `TransactionData` with gas fees, and then signs `TransactionData`. - 1. The sponsor sends the signed `TransactionData` and the sponsor `Signature` back to the user. - 1. The user verifies and then signs `TransactionData` and sends the dual-signed transaction to IOTA network through a Full node or the sponsor. - -### GasLessTransactionData - -`GasLessTransactionData` is basically `TransactionData` without `GasData`. It is not a `iota-core` data structure, but it is only an interface between user and sponsor. - -The following example constructs a `GasLessTransactionData` object. - -```rust -pub struct GasLessTransactionData { - pub kind: TransactionKind, - sender: IotaAddress, - … -} -``` - -## Create a sponsor-initiated sponsored transaction - -A sponsor-initiated sponsored transaction involves the following steps: - 1. A sponsor constructs a `TransactionData` object that contains the transaction details and associated gas fee data. The sponsor signs it to generate a `Signature` before sending it to a user. You can send the unsigned `TransactionData` via email, SMS, or an application interface. - 1. The user checks the transaction and signs it to generate the second `Signature` for the transaction. - 1. The user submits the dual-signed transaction to a IOTA Full node or sponsor to execute it. - -You can use a sponsor-initiated sponsored transaction as an advertiser, or to incentivize specific user actions without requiring the user to pay for gas fees. - -## Create sponsored transactions using a GasData object - -To use a `GasData` object to sponsor the gas fees for a transaction, create a `GasData` object that covers the fees determined for the transaction. This is similar to providing a blank check to a user that can be used only to cover gas fees. The user doesn't need to know how much the fee is or approve it. - - A sponsor transaction using a `GasData` object involves the following steps: - 1. The sponsor provides a `GasData` object to a user. - 1. The user constructs `TransactionData` and signs it to generate a `Signature`. - 1. The user sends the `TransactionData` and the `Signature` to the sponsor. - 1. The sponsor confirms the `TransactionData` and then signs it. - 1. The sponsor submits the dual-signed `TransactionData` to a Full node to execute the transaction. - -## Create an IOTA gas station - -On IOTA, a gas station is a concept to describe where you set up processes to sponsor user transactions. You can customize a IOTA gas station to support the specific user-facing functionality you need. Some example use cases for a IOTA gas station include: - -- Monitor real-time gas prices on the network to determine the gas price that the station provides. -- Track usage of gas provided to users on the network. -- Gas pool management, such as using specific gas objects to minimize costs or reduce the risk of a large amount of locked objects that remain illiquid while locked. - -### Authorization and rate limiting - -Depending on the nature of your gas station, you can apply different authorization rules to avoid being spammed by bad actors. Possible policies include: - -- Rate limit gas requests per account or per IP address -- Only accept requests with a valid authorization header, which has separate rate limits - -### Abuse detection - -For all gas objects that you provide as a sponsor, you should track if users ever try to equivocate and lock objects. If you detect such behavior, block the user or requester accordingly. - -## Code examples to create a IOTA gas station - -The following Rust SDK code examples demonstrate how to implement a IOTA gas station that supports each of the sponsored transaction types described previously. - -### User-initiated sponsored transactions - -Use the API endpoint to receive `GaslessTransaction` transactions and return a sole-signed `SenderSignedData` object. - -```rust -pub fn request_gas_and_signature(gasless_tx: GaslessTransaction) -> Result; -``` - -### Sponsored transactions with GasData objects - -Use the API endpoint to receive sole-signed `SenderSignedData` and return the result of the transaction. - -```rust -pub fn submit_sole_signed_transaction(sole_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>; -``` - -Alternatively, use the API endpoint to return a GasData object. - -```rust -pub fn request_gas(/*requirement data*/) -> Result; -``` - -### User and sponsor-initiated transaction - -Use the API endpoint to receive dual-signed `SenderSignedData` and return the result of the transaction. - -```rust -pub fn submit_dual_signed_transaction(dual_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>; -``` - -For user and sponsor-initiated transactions, users can submit the dual-signed transaction via either a sponsor or a Full node. - -## Sponsored transaction data structure - -The following code block describes the `TransactionData` structure for sponsored transactions and `GasObject`. You can view the [source code](https://github.com/iotaledger/iota/blob/develop/crates/iota-types/src/messages.rs) in the IOTA GitHub repository. - -**`TransactionData` Structure** -```rust -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct TransactionDataV1 { -pub kind: TransactionKind, -pub sender: IotaAddress, -pub gas_data: GasData, -pub expiration: TransactionExpiration, -} -``` - -**`GasData` Structure** -```rust -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct GasData { - pub payment: Vec, - pub owner: IOTAAddress, - pub price: u64, - pub budget: u64, -} -``` - -To learn more about transactions in IOTA, see [Transactions](transactions.mdx). - - \ No newline at end of file diff --git a/docs/content/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.mdx b/docs/content/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.mdx new file mode 100644 index 00000000000..090939ee168 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.mdx @@ -0,0 +1,151 @@ +--- +description: A guide to understanding and implementing sponsored transactions on the IOTA blockchain, including roles, use cases, workflows, and risk considerations. +--- +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.json'; + +# Sponsored Transactions on IOTA + +Sponsored transactions are a powerful feature in the IOTA blockchain that allow transactions to be executed without the user directly paying for gas. + +## Understanding Sponsored Transactions + +On the IOTA network, executing a transaction requires a gas payment, which is a list of [`0x2::coin::Coin<0x2::iota::IOTA>`](../../../../references/framework/iota-framework/coin.mdx) objects paid to validators to secure the network. +While gas fees are essential to the [IOTA tokenomics](../../../../about-iota/tokenomics/tokenomics.mdx), they can present challenges for new users, especially those accustomed to traditional web applications. + +Sponsored transactions aim to simplify the onboarding process by allowing transactions to be executed without the user having to provide the gas payment themselves. Instead, a sponsor covers the gas fees, enhancing the user experience and reducing friction. + +## Key Roles in Sponsored Transactions + +There are three main participants in a sponsored transaction: + +- **User**: The individual or entity initiating the transaction. +- **Gas Station**: The service that processes sponsorship requests and provides the gas payment owned by the sponsor. +- **Sponsor**: The entity funding the gas station, supplying the gas payments used in transactions. + +In many cases, the gas station and the sponsor are the same entity. +For example, a web3 gaming company might operate its own gas station to sponsor user transactions, +offering a seamless, free-to-play experience to attract new players. +Alternatively, they might use third-party gas stations to promote certain transactions. + +For the purposes of this guide, we'll assume that the sponsor operates their own gas station. + +## Common Use Cases + +Sponsored transactions enhance the user experience in various scenarios. Here are some typical examples: + +### Application-Specific Sponsorship + +In this scenario, the sponsor targets specific applications or transactions for sponsorship. + +- **User-Initiated Transactions**: The sponsor reviews the user's transaction to ensure it aligns with approved applications before providing the gas payment. +- **Sponsor-Initiated Transactions**: The user reviews a transaction proposed by the sponsor and decides whether to execute it. Examples include claiming rewards from a campaign or testing out a new feature. + +### General Sponsorship + +Here, the sponsor places minimal restrictions on the types of transactions they are willing to sponsor. + +- **Gasless Wallets**: The sponsor may agree to cover gas fees for any valid transactions initiated by users. +- **Rewards and Promotions**: The sponsor provides a wildcard gas payment, allowing users to execute transactions freely as part of a promotion or loyalty program. + +Sponsored transactions are not limited to these scenarios. Essentially, any transaction that involves collaboration between the user and the sponsor can be facilitated through sponsorship, provided both parties agree on the details. However, involving multiple stakeholders introduces certain [risks](#risk-considerations) that need to be managed. + +## Workflow of Sponsored Transactions + +This section is intended for developers interested in building or integrating with a gas station. + +### Transaction Data Structure + +A transaction in IOTA has a specific data structure, which can be represented as: + +```rust +pub struct SenderSignedTransaction { +pub intent_message: IntentMessage, +/// A list of signatures signed by all transaction participants. +/// 1. Non-participant signatures must not be present. +/// 2. Signature order does not matter. +pub tx_signatures: Vec, +} + +pub struct TransactionDataV1 { // <-- A variant of `TransactionData` +pub kind: TransactionKind, // <-- This contains the transaction details +pub sender: IOTAAddress, +pub gas_data: GasData, +pub expiration: TransactionExpiration, +} + +pub struct GasData { +pub payment: Vec, +pub owner: IOTAAddress, +pub price: u64, +pub budget: u64, +} +``` + +Key points to note: + +- The `sender` in `TransactionDataV1` represents the user's address. +- The `gas_data` field contains the gas payment information. +- `GasData` can include multiple gas objects, but they must all be owned by the same address—the `owner` field, which is the sponsor. If the `owner` and `sender` are the same, it's a regular (non-sponsored) transaction. +- The `tx_signatures` array must include signatures from both the user and the sponsor for a sponsored transaction. These signatures cover the entire `TransactionData`, including `GasData`. + +To create a valid sponsored transaction: + +1. Build the `TransactionData` object. +2. Both the user and the sponsor must sign the transaction. + +Typically, the sponsor signs the transaction first and then sends it to the user to add their signature. Alternatively, if a third party is coordinating, they would collect signatures from both parties. + +### Transaction Flows + +There are three common workflows for sponsored transactions: + +#### User-Proposed Transaction + +In this flow, the user initiates the transaction and requests sponsorship. + +![User-Proposed Transaction](/img/developer/iota-101/transactions/sponsored-transactions/user-proposed-txn.png) + +#### Sponsor-Proposed Transaction + +Here, the sponsor initiates the transaction, and the user must approve and sign it. + +![Sponsor-Proposed Transaction](/img/developer/iota-101/transactions/sponsored-transactions/sponsored-txn.png) + +#### Wildcard Gas Payment + +In this scenario, the sponsor provides a gas payment that the user can use for any transaction. + +![Wildcard Gas Payment](/img/developer/iota-101/transactions/sponsored-transactions/wildcard-txn.png) + +## Risk Considerations + +When multiple stakeholders are involved in a transaction, it's important to be aware of potential risks and take steps to mitigate them. + +### Client Equivocation Risk + +Client equivocation occurs when multiple valid transactions sharing at least one owned object (e.g., a gas coin) are submitted to the network at the same time. +On IOTA, owned objects are locked at specific versions before transaction execution. +Honest validators will only accept one transaction and reject the rest. +If validators receive transactions in different orders, it can lead to inconsistencies, +and if no transaction is accepted by at least two-thirds of validators, the owned object remains locked until the end of the epoch. + +While client equivocation is rare and often due to software bugs, sponsored transactions introduce counterparty risks. +A malicious user might submit conflicting transactions using the sponsor's gas coin, +or a rogue gas station could do the same with the user's owned objects. + +To mitigate this risk: + +- Gas stations should monitor user activity and flag any suspicious behavior. +- Both parties must sign the entire `TransactionData`, including `GasData`, to prevent third parties from intercepting and altering the transaction data, which could cause client equivocation. + +### Censorship Risk + +If you submit the dual-signed transaction to the sponsor or gas station instead of directly to a full node, +there's a risk of censorship or delayed submission by the sponsor. + +To avoid this: + +- Submit the fully signed transaction directly to a full node yourself. + + diff --git a/docs/content/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.mdx b/docs/content/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.mdx new file mode 100644 index 00000000000..59a8fd18849 --- /dev/null +++ b/docs/content/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.mdx @@ -0,0 +1,129 @@ +import Quiz from '@site/src/components/Quiz'; +import questions from '/json/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.json'; + +# Use Sponsored Transactions + +[Sponsored transactions](about-sponsored-transactions.mdx) in IOTA enable one address (the sponsor) to pay the gas fees for a [transaction](../transactions.mdx) initiated by another address (the user). + +You can leverage IOTA sponsored transactions to: + +- **Sponsor user-initiated transactions**: Pay the gas fees for transactions that users initiate. +- **Sponsor your own transactions**: Cover the gas fees for transactions you start. +- **Provide a wildcard `GasData` object**: Offer users a `GasData` object that covers gas fees for their transactions, as long as the budget is sufficient. + + +:::danger Risks of Sponsored Transactions + +While sponsored transactions offer significant benefits, they come with [potential risks](about-sponsored-transactions.mdx#client-equivocation-risk), the most notable being [equivocation](/references/iota-glossary.mdx#equivocation). Under certain conditions, a sponsored transaction can cause all associated owned objects, including gas, to become locked when processed by IOTA validators. + +::: + +## User-Initiated Sponsored Transaction Flow + +To set up a user-initiated sponsored transaction, follow these steps: + +1. **User creates a `GasLessTransactionData` transaction**: The user initializes a transaction without gas data. +2. **User sends `GasLessTransactionData` to the sponsor**: The user forwards the transaction data to the sponsor. +3. **Sponsor validates and signs**: The sponsor reviews the transaction, constructs `TransactionData` with gas fees, and signs it. +4. **Sponsor returns signed transaction**: The sponsor sends the signed `TransactionData` and their `Signature` back to the user. +5. **User verifies and signs**: The user verifies the transaction, signs the `TransactionData`, and submits the dual-signed transaction to the IOTA network via a Full node or through the sponsor. + +### Understanding `GasLessTransactionData` + +`GasLessTransactionData` is essentially a `TransactionData` structure without the `GasData` field. It is not an official `iota-core` data structure but serves as an interface between the user and the sponsor. + +Here is an example of how a `GasLessTransactionData` object might be constructed: + +```rust +pub struct GasLessTransactionData { +pub kind: TransactionKind, +sender: IotaAddress, +… +} +``` + +## IOTA Gas Station + +An IOTA gas station is a concept where you set up processes to sponsor user transactions. +You can customize a gas station to provide specific functionalities based on your needs. Some examples include: + +- **Monitoring gas prices**: Keep track of real-time gas prices on the network to determine the gas price offered. +- **Tracking gas usage**: Monitor how the gas provided to users is utilized on the network. +- **Managing gas pools**: Use specific gas objects to minimize costs or reduce the risk of having a large number of locked objects that remain illiquid while locked. + +### Implement Authorization and Rate Limiting + +To prevent abuse, you can implement various authorization rules: + +- **Rate limiting**: Limit gas requests per account or IP address. +- **Authentication**: Accept requests only with a valid authorization header, each having separate rate limits. + +### Detect and Prevent Abuse + +Monitor all gas objects provided as a sponsor to detect if users attempt to equivocate or lock objects. If such behavior is detected, block the user or requester accordingly. + +### Code Examples for Creating an IOTA Gas Station + +The following Rust SDK code examples demonstrate how to implement an IOTA gas station that supports the various types of sponsored transactions discussed earlier. + +#### User-Initiated Sponsored Transactions + +Use the API endpoint to receive `GaslessTransaction` transactions and return a sole-signed `SenderSignedData` object: + +```rust +pub fn request_gas_and_signature(gasless_tx: GaslessTransaction) -> Result; +``` + +#### Sponsored Transactions with `GasData` Objects + +Use the API endpoint to receive a sole-signed `SenderSignedData` and return the result of the transaction: + +```rust +pub fn submit_sole_signed_transaction(sole_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>; +``` + +Alternatively, provide a `GasData` object via an API endpoint: + +```rust +pub fn request_gas(/*requirement data*/) -> Result; +``` + +#### Sponsor-Initiated Transactions + +Use the API endpoint to receive dual-signed `SenderSignedData` and return the transaction result: + +```rust +pub fn submit_dual_signed_transaction(dual_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>; +``` + +For user and sponsor-initiated transactions, users can submit the dual-signed transaction either through the sponsor or directly to a Full node. + +#### Data Structures for Sponsored Transactions + +The following code blocks describe the `TransactionData` structure for sponsored transactions and the `GasData` structure. You can view the [source code](https://github.com/iotaledger/iota/blob/develop/crates/iota-types/src/messages.rs) in the IOTA GitHub repository. + +##### `TransactionData` Structure + +```rust +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct TransactionDataV1 { +pub kind: TransactionKind, +pub sender: IotaAddress, +pub gas_data: GasData, +pub expiration: TransactionExpiration, +} +``` + +##### `GasData` Structure + +```rust +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct GasData { +pub payment: Vec, +pub owner: IOTAAddress, +pub price: u64, +pub budget: u64, +} +``` + + diff --git a/docs/content/developer/iota-101/transactions/transactions.mdx b/docs/content/developer/iota-101/transactions/transactions.mdx index 445ec5ae7a2..905a528a5dc 100644 --- a/docs/content/developer/iota-101/transactions/transactions.mdx +++ b/docs/content/developer/iota-101/transactions/transactions.mdx @@ -1,44 +1,66 @@ --- -title: Transactions +description: An introduction to transactions, their types, metadata, and execution flow in Move-based blockchain networks. --- import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/transactions/transactions.json'; -All updates to the IOTA database happen via transactions. This topic describes the transaction types supported by IOTA and explains how their execution changes the ledger. ´There are only two kinds of transactions on IOTA: +# Transactions -- Programmable transaction blocks, which anyone can submit on the network. For information on these transactions, see [Programmable Transaction Blocks](ptb/prog-txn-blocks.mdx). -- System transactions, which only validators can directly submit and are responsible for keeping the network running (changing epochs, starting checkpoints, and so on). +Transactions are fundamental to updating the ledger in Move-based blockchains. -## Transaction metadata +## Types of Transactions -All IOTA transactions have the following common metadata: +In Move, there are two primary types of transactions: -- **Sender address:** The [address](../../getting-started/get-address.mdx) of the user sending this transaction. -- **Gas input:** An object reference pointing to the object that will be used to pay for this transaction's execution and storage. This object must be owned by the user and must be of type `iota::coin::Coin` (i.e., the IOTA native currency). -- **Gas price:** An unsigned integer specifying the number of native tokens per gas unit this transaction will pay. The gas price must always be nonzero. -- **Maximum gas budget:** The maximum number of gas units that can be expended by executing this transaction. If this budget is exceeded, transaction execution will abort and have no effects other than debiting the gas input object. Consequently, the gas input object must have a value higher than the gas price multiplied by the max gas, and this product is the maximum amount that the gas input object will be debited for the transaction. -- **Epoch:** The IOTA epoch this transaction is intended for. -- **Type:** A call, publish, or native transaction and its type-specific-data (see below). -- **Authenticator:** A cryptographic signature and a public key that both verifies against the signature and is cryptographically committed to by the sender address. -- **Expiration:** An epoch reference that sets a deadline after which validators will no longer consider the transaction valid. The optional expiration epoch reference enables users to define transactions that either execute and commit by a set time (current epoch less than or equal to expiration epoch), or never execute after the deadline passes. By default, there is no deadline for when a transaction must execute. +- **User Transactions**: [Programmable transaction blocks](ptb/programmable-transaction-blocks) that any user can submit to the network. They allow you to execute custom logic, interact with smart contracts, and transfer assets. +- **System Transactions**: Exclusive to validators, these transactions are essential for network operations like epoch transitions and checkpointing. They help maintain the blockchain's integrity and performance. -## Transactions flow - example +## Key Components of a Transaction -Here's an example showing how objects and transactions are connected to each other in IOTA. +Every transaction in Move includes essential metadata: -In the following example there are two objects: -- Object A is a coin of type IOTA with a total balance of 5 IOTA -- Object B with 2 IOTA coins that belongs to John +- **Sender Address**: The [account address](../../getting-started/get-address.mdx) initiating the transaction. +- **Gas Payment Object**: An object reference used to pay for the transaction's execution and storage costs. This object must be owned by the sender and be of the type `move::coin::Coin`, representing the native currency. +- **Gas Price**: The amount of native tokens per unit of gas the sender is willing to pay. This must be a positive integer. +- **Maximum Gas Budget**: The upper limit of gas units the transaction can consume. Exceeding this budget will abort the transaction, affecting only the gas payment object by deducting the consumed gas. +- **Target Epoch**: The specific epoch for which the transaction is intended. +- **Transaction Type**: Specifies whether it's a call, publish, or native transaction, along with its associated data. +- **Authenticator**: A cryptographic signature and public key pair that verifies the sender's identity. +- **Expiration Epoch**: An optional deadline after which validators will consider the transaction invalid if not yet executed. By default, transactions have no expiration. -Tom decides to send 1 IOTA coin to Alice. In this case, Object A is the input to this transaction and 1 IOTA coin is debited from this object. The output of the transaction is two objects: -- Object A with 4 IOTA coins that still belongs to Tom -- new created Object C with 1 IOTA coin that belongs now to Alice +## Transaction Workflow Example -At the same time, John decides to send 2 IOTA coins to Anna. Because the relationship between objects and transactions is written in a directed acyclic graph (DAG), and both transactions interact with different objects, this transaction executes in parallel with the transaction that sends coins from Tom to Alice. This transaction changes only the owner of Object B from John to Anna. +To illustrate how transactions interact with objects, consider the following scenario involving IOTA: -After receiving 2 IOTA coins, Anna sent them immediately to Tom. Now Tom has 6 IOTA coins (4 from Object A and 2 from Object B). +### Initial Setup -Finally, Tom sends all of his IOTA coins to John. For this transaction, the input is actually two objects (Object A and Object B). Object B is destroyed, and its value is added to Object A. As a result, the transaction's output is only Object A with a value of 6 IOTA. +- **Object A**: Contains 5 IOTA and belongs to Tom. +- **Object B**: Holds 2 IOTA and belongs to John. + +### 1: Tom Sends 1 IOTA to Alice + +Tom initiates a transaction to send 1 IOTA to Alice using Object A. The transaction results in: + +- **Object A**: Now has 4 IOTA and remains with Tom. +- **Object C**: A new object with 1 IOTA belonging to Alice. + +### 2: John Sends 2 IOTA to Anna + +Simultaneously, John sends his 2 IOTA to Anna. Since this transaction involves different objects, it executes in parallel, transferring ownership of Object B to Anna. + +### 3: Anna Sends 2 IOTA to Tom + +Anna immediately sends the 2 IOTA to Tom. Tom now possesses: + +- **Object A**: 4 IOTA. +- **Object B**: 2 IOTA (ownership transferred to Tom). + +### 4: Tom Sends All IOTA to John + +Finally, Tom decides to send all his IOTA to John. The transaction consumes both Object A and Object B, combining them into: + +- **Object D**: A new object with 6 IOTA belonging to John. +- **Object A** and **Object B**: Consumed and removed from the ledger. ```mermaid flowchart LR @@ -49,24 +71,30 @@ flowchart LR id5(Object B\nfa:fa-coins 2 IOTA\n fa:fa-person Anna):::object-b; id6(Object B\nfa:fa-coins 2 IOTA\n fa:fa-person Tom):::object-b; id7(Object A\nfa:fa-coins 6 IOTA\n fa:fa-person John):::object-a; - id1-->|tx-1|id2; - id1-->|tx-1|id3; - id4-->|tx-2|id5; - id5-->|tx-3|id6; - id3-->|tx-4|id7; - id6-->|tx-4|id7; - classDef object-a fill:#f225; - classDef object-b fill:#ff43; +id1-->|tx-1|id2; +id1-->|tx-1|id3; +id4-->|tx-2|id5; +id5-->|tx-3|id6; +id3-->|tx-4|id7; +id6-->|tx-4|id7; +classDef object-a fill:#f225; +classDef object-b fill:#ff43; ``` -## Limits on transactions, objects, and data +## Transaction and Data Constraints + +Move imposes certain limits to ensure network stability and security: + +- **Maximum Transaction Size**: Transactions have a byte-size limit to prevent excessive resource consumption. +- **Object Limits**: There's a cap on the number of objects a transaction can read or modify. +- **Data Size Restrictions**: Limits are placed on the size of data payloads within transactions. -IOTA has some limits on transactions and the data used in them, such as a maximum size and number of objects used. You can find the these limits in the [`iota-protocol-config` crate](https://github.com/iotaledger/iota/blob/develop/crates/iota-protocol-config/src/lib.rs) of the IOTA repo. The limits are defined in the `ProtocolConfig` struct and values set in the `get_for_version_impl` function. +You can find detailed limits in the [`protocol-config` module](https://github.com/move-language/move/blob/main/language/move-prover/bytecode/src/protocol_config.rs) of the Move repository, defined in the `ProtocolConfig` struct. -## Transactions Execution Flow +## How Transactions Are Executed -The following diagram shows the transaction execution flow reflected by functions called inside different modules. +The following diagram outlines the execution flow of a transaction, showing the sequence of function calls across various modules: -![Transactions execution flow](/img/concepts/execution-architecture/tx-exec-flow.svg) +![Transaction Execution Flow](/img/concepts/execution-architecture/tx-exec-flow.svg) - \ No newline at end of file + diff --git a/docs/content/developer/iota-101/using-events.mdx b/docs/content/developer/iota-101/using-events.mdx index 3b00a9ac9ae..f808f9e1e07 100644 --- a/docs/content/developer/iota-101/using-events.mdx +++ b/docs/content/developer/iota-101/using-events.mdx @@ -1,7 +1,6 @@ --- description: Learn how to monitor IOTA on-chain activity by subscribing to events emitted by Move packages. --- -import AlphaNet from "../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/using-events.json'; @@ -109,8 +108,6 @@ This example leverages the IOTA TypeScript SDK to subscribe to events the packag ### Rust - - See [Rust SDK](../../references/rust-sdk.mdx). ```rust @@ -122,8 +119,8 @@ use anyhow::Result; #[tokio::main] async fn main() -> Result<()> { let iota = IOTAClientBuilder::default() - .ws_url("wss://fullnode.mainnet.iota.io:443") - .build("https://fullnode.mainnet.iota.io:443") + .ws_url("wss://api.testnet.iota.cafe:443") + .build("https://api.testnet.iota.cafe:443") .await.unwrap(); let mut subscribe_all = iota.event_api().subscribe_event(EventFilter::All(vec![])).await?; loop { diff --git a/docs/content/developer/iota-evm/explanations/consensus.md b/docs/content/developer/iota-evm/explanations/consensus.md deleted file mode 100644 index 27d3f3a07a2..00000000000 --- a/docs/content/developer/iota-evm/explanations/consensus.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -description: IOTA Smart Contracts consensus is how Layer 2 validators agree to change the chain state in the same way. -image: /img/logo/WASP_logo_dark.png -tags: - - consensus - - validator - - explanation - - evm ---- - -# Consensus - -To update the chain, its committee must reach a _consensus_, meaning that more than two thirds of its validators have to -agree to change the state in the exact same way. -This prevents a single malicious node from wreaking havoc over the chain, but there are also more mundane reasons for -individual nodes to act up. - -Smart contracts are deterministic. All honest nodes will produce the same output — but only if they have received the -same input. Each validator node has its point of access to the Tangle, so it may look different to different nodes, as -new transactions take time to propagate through the network. Validator nodes will receive smart contract requests with -random delays in a random order, and, finally, all computers run on their own always slightly skewed clocks. - -## Batch Proposals - -As the first step, each node provides its vision, a _batch proposal_. The proposal contains a local timestamp, a list of -unprocessed requests, and the node's partial signature of the commitment to the current state. - -Then the nodes must agree on which batch proposals they want to work on. In short, nodes A, B, and C have to confirm -that they plan to work on proposals from A, B, and C, and from no one else. As long as there are more than two thirds of -honest nodes, they will be able to find an _asynchronous common subset_ of the batch proposals. From that point, nodes -have the same input and will produce the same result independently. - -## The Batch - -The next step is to convert the raw list of batch proposals into an actual batch. All requests from all proposals are -counted and filtered to produce the same list of requests in the same order. -The partial signatures of all nodes are combined into a full signature that is then fed to a pseudo-random function that -sorts the smart contract requests. -Validator nodes can neither affect nor predict the final order of requests in the batch. (This protects ISC -from [MEV attacks](https://ethereum.org/en/developers/docs/mev/)). - -## State Anchor - -After agreeing on the input, each node executes every smart contract request in order, independently producing the same -new block. Each node then crafts a state anchor, a Layer 1 transaction that proves the commitment to this new chain -state. The timestamp for this transaction is derived from the timestamps of all batch proposals. - -All nodes then sign the state anchor with their partial keys and exchange these signatures. This way, every node obtains -the same valid combined signature and the same valid anchor transaction, which means that any node can publish this -transaction to Layer 1. In theory, nodes could publish these state anchors every time they update the state; in -practice, they do it only every approximately ten seconds to reduce the load on the Tangle. diff --git a/docs/content/developer/iota-evm/explanations/context.mdx b/docs/content/developer/iota-evm/explanations/context.mdx deleted file mode 100644 index f2fc48ae68e..00000000000 --- a/docs/content/developer/iota-evm/explanations/context.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: The call context is a predefined parameter to each smart contract function, which allows you to access the functionality that the call environment provides. -tags: - - explanation - - evm -image: /img/logo/WASP_logo_dark.png ---- - -# Call Context - -Understanding the call context is vital in leveraging the Wasm code within a sandboxed host environment effectively. The following section explains the distinction between different function calls and the role of WasmLib in setting up a smart contract. - -## Function Calls: Func and View - -Smart contract function calls come in two types, each having specific access levels to the smart contract state: - -### Funcs - -Func functions grants full mutable access, resulting in a state update. They accommodate both on-ledger and off-ledger requests, finalized once the state update is registered in the Tangle ledger. - -### Views - -View functions allow limited immutable access, **no state update occurs**. Views are ideal for quickly querying the contract's current state, they only facilitate off-ledger calls. - -## Using Func and View - -WasmLib offers distinct contexts for these function types, namely `ScFuncContext` for Func and `ScViewContext` for View, controlling the accessible functionality and enforcing usage constraints through compile-time type-checking. - -## Smart Contract Setup with WasmLib - -Setting up a smart contract requires the following: - -### Defining Funcs and Views - -Outline available Funcs and Views and communicate them to the host through WasmLib. -It ensures the correct dispatch of function calls and maintains necessary restrictions. - -### Parameter and Return Value Determination - -Establish the parameters and return values for each function. ISC uses simple dictionaries to store details, necessitating consistent (de)serialization handled adeptly by WasmLib. - -### Utilizing Schema Tool - -Although you can use the WasmLib directly, the Schema Tool is recommended for automatically generating and updating the smart contract framework in a type-safe manner, using the preferred language. - -Grasping these concepts will facilitate a secure and efficient smart contract setup, steering clear of potential pitfalls while making the most of what WasmLib offers. diff --git a/docs/content/developer/iota-evm/explanations/core-contracts.md b/docs/content/developer/iota-evm/explanations/core-contracts.md deleted file mode 100644 index 2aa793c4c0e..00000000000 --- a/docs/content/developer/iota-evm/explanations/core-contracts.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -description: There currently are 6 core smart contracts that are always deployed on each chain, root, _default, accounts, blob, blocklog, and governance. -image: /img/banner/banner_wasp_core_contracts_overview.png -tags: - - core-contract - - reference - - evm ---- - -# Core Contracts - -![Wasp Node Core Contracts Overview](/img/banner/banner_wasp_core_contracts_overview.png) - -There are currently 7 core smart contracts that are always deployed on each -chain. These are responsible for the vital functions of the chain and -provide infrastructure for all other smart contracts: - -- [`root`](../../../references/iota-evm/core-contracts/root.md): Responsible for the initialization of the chain, maintains registry of deployed contracts. - -- [`accounts`](../../../references/iota-evm/core-contracts/accounts.md): Manages the on-chain ledger of accounts. - -- [`blob`](../../../references/iota-evm/core-contracts/blob.md): Responsible for the registry of binary objects of arbitrary size. - -- [`blocklog`](../../../references/iota-evm/core-contracts/blocklog.md): Keeps track of the blocks and receipts of requests that were processed by the chain. - -- [`governance`](../../../references/iota-evm/core-contracts/governance.md): Handles the administrative functions of the chain. For example: rotation of the committee of validators of the chain, fees and other chain-specific configurations. - -- [`errors`](../../../references/iota-evm/core-contracts/errors.md): Keeps a map of error codes to error messages templates. These error codes are used in request receipts. - -- [`evm`](../../../references/iota-evm/core-contracts/evm.md): Provides the necessary infrastructure to accept Ethereum - transactions and execute EVM code. diff --git a/docs/content/developer/iota-evm/explanations/how-accounts-work.md b/docs/content/developer/iota-evm/explanations/how-accounts-work.md deleted file mode 100644 index fc55265c225..00000000000 --- a/docs/content/developer/iota-evm/explanations/how-accounts-work.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -description: IOTA Smart Contracts chains keep a ledger of on-chain account balances. On-chain accounts are identified by an AgentID. -image: /img/iota-evm/tutorial/accounts.png -tags: - - explanation - - isc-accounts - - evm ---- - -# How Accounts Work - -On the L1 Ledger, like with any _DLT_, we have **trustless** and **atomic** transfers of assets between addresses on the -ledger. - -Tokens controlled by an address can be moved to another address by providing a valid signature using the private key -that controls the source address. - -## L1 Addresses - -In IOTA Smart Contracts, [each chain has a L1 address](../explanations/states.md#digital-assets-on-the-chain) (also known as the _Chain -ID_) which enables it to control L1 assets (base tokens, native tokens and NFTs). -The chain acts as a custodian of the L1 assets on behalf of different entities, thus providing a _L2 Ledger_. - -## L2 Accounts - -The L2 ledger is a collection of _on-chain accounts_ (sometimes called just _accounts_). -L2 accounts can be owned by different entities, identified by a unique _Agent ID_. -The L2 ledger is a mapping of Agent ID => balances of L2 assets. - -## Types of Accounts - -### L1 Address - -Any L1 address can be the owner of a L2 account. -The Agent ID of an L1 address is just the address, - -e.g. `iota1pr7vescn4nqc9lpvv37unzryqc43vw5wuf2zx8tlq2wud0369hjjugg54mf`. - -Tokens in an address account can only be moved through a request signed by the private key of the L1 address. - -### Smart Contract - -Any _smart contract_ can be the owner of a L2 account. Recall that a smart -contract is uniquely identified in a chain by a [_hname_](smart-contract-anatomy.md#identifying-a-smart-contract). -However, the hname is not enough to identify the account since a smart contract on another chain could own it. - -Thus, the Agent ID of a smart contract is composed as the contract hname plus the [_chain ID_](states.md#digital-assets-on-the-chain), with syntax `@`. -For example: `cebf5908@tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd`. - -Note that this allows trustless transfers of assets between smart contracts on the same or different chains. - -Tokens in a smart contract account can only be moved by that smart contract. - -### The Common Account - -The chain owns a unique L2 account, called the _common account_. -The common account is controlled by the chain owner (defined in the chain root contract) and is used to store funds -collected by fees or sent to the chain L1 address. - -The Agent ID of the common account is `@
`. - -### Ethereum Address - -An L2 account can also be owned by an Ethereum address. See [EVM](../introduction.mdx) for more information. -The Agent ID of an Ethereum address is just the address prefixed with `0x`, -e.g. `0xd36722adec3edcb29c8e7b5a47f352d701393462`. - -Tokens in an Ethereum account can only be moved by sending an Ethereum transaction signed by the same address. - -## The Accounts Contract - -The [`accounts` core contract](../../../references/iota-evm/core-contracts/accounts.md) is responsible for managing the L2 ledger. -By calling this contract, it is possible to: - -- [View current account balances](../how-tos/core-contracts/basics/get-balance.md) -- [Deposit funds to the chain](../how-tos/send-funds-from-L1-to-L2.mdx) -- [Withdraw funds from the chain](../how-tos/core-contracts/basics/send-assets-to-l1.mdx) - -## Example - -The following diagram illustrates an example situation. -The IDs and hnames are shortened for simplicity. - -[![Example situation. Two chains are deployed, with three smart contracts and one address.](/img/iota-evm/tutorial/accounts.png)](/img/iota-evm/tutorial/accounts.png) - -Two chains are deployed, with IDs `chainA` and `chainB`. -`chainA` has two smart contracts on it (with hnames `3037` and `2225`), and `chainB` has one smart contract (`7003`). - -There is also an address on the L1 Ledger: `iota1a2b3c4d`. -This address controls 1337 base tokens and 42 `Red` native tokens on the L1 Ledger. - -The same address also controls 42 base tokens on `chainA` and 8 `Green` native tokens on `chainB`. - -So, the owner of the private key behind the address controls three different accounts: the L1 account and one L2 account -on each chain. - -Smart contract `7003@chainB` has five base tokens on its native chain and controls eleven base tokens on chain A. diff --git a/docs/content/developer/iota-evm/explanations/invocation.mdx b/docs/content/developer/iota-evm/explanations/invocation.mdx deleted file mode 100644 index d8590794a66..00000000000 --- a/docs/content/developer/iota-evm/explanations/invocation.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -description: Smart contracts can be invoked through their entry points, from outside via a request, or from inside via a call. -image: /img/logo/WASP_logo_dark.png -tags: - - on-ledger - - off-ledger - - explanation - - evm ---- - -import OnOffLedgerRequest from '../../../_snippets/iota-evm/on_off_ledger_request.md'; - -# Calling a Smart Contract - -## Entry Points - -Like any other computer program, a smart contract will lie dormant until someone or something instructs it to activate. -In the case of smart contracts, the most common way to activate them is to call one of -their [entry points](./smart-contract-anatomy.md#entry-points). It is the same as calling a program's function. It will -take a set of instructions of the smart contract and execute it over the current chain's state. _View entry points_ can -only read the state, while _full entry points_ can read and write to it. - -To invoke a smart contract from outside the chain, the _sender_ (some entity that needs to be identified by a -private/public key pair) has to wrap the call to the entry point into a _request_. -The request has to be cryptographically signed and submitted to the [consensus](./consensus.md) procedure to let the -chain's committee evaluate it and engrave the outcome of its execution into a new state update. - -Upon receiving a request, the committee will execute the wrapped call fully or reject the request with all its potential -changes, never modifying the state halfway. This means that every single request is an atomic operation. - -### Synchronous Composability - -After being invoked by a request, the smart contract code is allowed to invoke entry points of other smart contracts on -the same chain. This means it can _call_ other smart contracts. - -Smart contract calls are deterministic and synchronous, meaning they always produce the same result and execute all -instructions immediately after another. -If a smart contract calls another smart contract, the resulting set of instructions is also deterministic and -synchronous. This means that for a request, it makes no difference if a smart contract's entry point contains the whole -set of instructions or if it is composed by multiple calls to different smart contracts of the chain. - -Being able to combine smart contracts in this way is called _synchronous composability_. - ---- - -## Requests - -A request contains a call to a smart contract and a signature of the sender. The sender also owns the assets and funds -processed within the request. -Unlike calls between smart contracts, requests are not executed immediately. -Instead, they must wait until the chain's _validator_ nodes include them in a request batch. -This means that requests have a delay and are executed in an unpredictable order. - -### Asynchronous Composability - -Requests are not sent by humans exclusively. Smart contracts can also create requests. -For example, a user can send a request to a smart contract that, in turn, sends a request to a decentralized third-party -exchange which would will the user's funds from one currency to another and send them back through another request. - -This is called _asynchronous composability_. - - - ---- - -## Gas - -Gas expresses the "cost" of running a request in a chain. Each operation (arithmetics, write to disk, dispatch events, -etc.) has an associated gas cost. The amount of gas required for a transaction depends on the complexity of the -operation. For example, simple transfers may require less gas, while interacting with smart contracts for actions such -as token swaps can require more due to the higher computational work involved. - -For users to specify how much they're willing to pay for a request, they need to specify a `GasBudget` in the request. -This gas budget is the "maximum operations that this request can execute" and will be charged as a fee based on the -chain's current [fee policy](../../../references/iota-evm/core-contracts/governance.md#fee-policy). - -The funds to cover the gas used will be charged directly from the user's on-chain account. - ---- - -## Allowance - -An allowance is a feature within smart contracts that controls how much one address can spend on behalf -of another address. Before a third party can withdraw tokens from your account, you must explicitly set an allowance for -that third party's address, specifying the maximum amount of tokens they are allowed to transfer. This mechanism is used -in various decentralized finance (DeFi) applications, where you might allow a smart contract to interact with your -tokens to participate in staking, lending, or trading activities. The original token owner can adjust or revoke the -allowance at any time, providing control over how your tokens are used by others. - -Any funds sent to the chain are credited to the sender's account. If you want a contract to use -those funds, you must specify an `Allowance` in the request. Contracts can then claim any of the allowed funds using -the sandbox `TransferAllowedFunds` function. - -The Allowance property looks like the following: - -```go -{ - BaseTokens: uint64 - NativeTokens: [{TokenID, uint256}, ...] - NFTs: [NFTID, ...] -} -``` diff --git a/docs/content/developer/iota-evm/explanations/sandbox.md b/docs/content/developer/iota-evm/explanations/sandbox.md deleted file mode 100644 index c2aba030c57..00000000000 --- a/docs/content/developer/iota-evm/explanations/sandbox.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: Smart Contracts can only interact with the world by using the Sandbox interface which provides limited and deterministic access to the state through a key/value storage abstraction. -image: /img/iota-evm/sandbox.png -tags: - - explanation - - evm ---- - -# Sandbox Interface - -A smart contract's access to the world has to be restricted. Imagine a smart contract that would directly tap into a -weather forecast website: as the weather changes, the result of the contract's execution will also change. This smart -contract is not deterministic, meaning that you cannot reproduce the result yourself to verify it because the result for -each execution could be different. - -The access to the chain's state has to be curated, too. The chain owner and developers of individual smart contracts are -not necessarily the same entity. A single malicious contract could ruin the whole chain if not limited to its own space. -Instead of working on the state as a whole, each smart contract can only modify a part of it. - -The only way for smart contracts to access data is to use the sandbox interface, which is deterministic. It provides -their internal state as a list of key/value pairs. - -![Sandbox](/img/iota-evm/sandbox.png) - -Besides reading and writing to the contract state, the Sandbox interface allows smart contracts to access: - -- The [ID](how-accounts-work.md) of the contract. -- The details of the current request or view call. -- The current request allowance and a way to claim the allowance. -- The balances owned by the contract. -- The ID of whoever had deployed the contract. -- The timestamp of the current block. -- Cryptographic utilities like hashing, signature verification, and so on. -- The [events](../schema/how-tos/events.mdx) dispatch. -- Entropy that emulates randomness in an unpredictable yet deterministic way. -- Logging. Used for debugging in a test environment. - -The Sandbox API available in "view calls" is slightly more limited than the one available in normal "execution calls". -For example, besides the state access being read-only for a view, they cannot issue requests, emit events, etc. diff --git a/docs/content/developer/iota-evm/explanations/smart-contract-anatomy.md b/docs/content/developer/iota-evm/explanations/smart-contract-anatomy.md deleted file mode 100644 index a2dcee5e5aa..00000000000 --- a/docs/content/developer/iota-evm/explanations/smart-contract-anatomy.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: Each smart contract instance has a program with a collection of entry points and a state. -image: /img/iota-evm/tutorial/SC-structure.png -tags: - - explanation - - evm ---- - -# Anatomy of a Smart Contract - -Smart contracts are programs that are immutably stored in the chain. - -Through _VM abstraction_, the ISC virtual machine is agnostic about the interpreter used to execute each smart contract. -It can support different _VM types_ (i.e., interpreters) simultaneously on the same chain. -For example, it is possible to have [Wasm](../getting-started/languages-and-vms.mdx#wasm-vm-for-isc) and [EVM/Solidity](../getting-started/languages-and-vms.mdx#what-is-evmsolidity) smart -contracts coexisting on the same chain. - -![Smart Contract Structure](/img/iota-evm/tutorial/SC-structure.png) - -## Identifying a Smart Contract - -The ISC [core contracts](core-contracts.md) and WASM contracts on the chain are identified by a _hname_ (pronounced -"aitch-name"), which is a `uint32` value calculated as a hash of the smart contract's instance name (a string). -For example, the hname of the [`root`](../../../references/iota-evm/core-contracts/root.md) core contract -is `0xcebf5908`. This value uniquely identifies this contract in every chain. This does not apply to EVM contracts. - -## State - -The smart contract state is the data owned by the smart contract and stored on the chain. -The state is a collection of key/value pairs. -Each key and value are byte arrays of arbitrary size (there are practical limits set by the underlying database, of -course). -You can think of the smart contract state as a partition of the chain's data state, which can only be written by the -smart contract program itself. - -The smart contract also owns an account on the chain, stored as part of the chain state. -The smart contract account represents the balances of base tokens, native tokens, and NFTs controlled by the smart -contract. - -The smart contract program can access its state and account through an interface layer called the _Sandbox_. -Only the smart contract program can change its data state and spend from its -account. Tokens can be sent to the smart contract account by any other agent on -the ledger, be it a wallet with an address or another smart contract. - -See [Accounts](./how-accounts-work.md) for more information on sending and receiving -tokens. - -## Entry Points - -Each smart contract has a program with a collection of _entry points_. -An entry point is a function through which you can invoke the program. - -There are two types of entry points: - -- _Full entry points_ (or simply _entry points_): These functions can modify - (mutate) the smart contract's state. -- _View entry points_ (or _views_): These are read-only functions. They are only used - to retrieve the information from the smart contract state. They cannot - modify the state, i.e., they are read-only calls. - -## Execution Results - -After a request to a Smart Contract is executed (a call to a full entry point), a _receipt_ will be added to -the [`blocklog`](../../../references/iota-evm/core-contracts/blocklog.md) core contract. The receipt details the -execution results -of said request: whether it was successful, the block it was included in, and other information. -Any events dispatched by the smart contract in context of this execution will also be added to the receipt. - -## Error Handling - -Smart contract calls can fail: for example, if they are interrupted for any reason (e.g., an exception) or if it -produces an error (missing parameter or other inconsistency). -Any gas spent will be charged to the sender, and the error message or value is stored in the receipt. diff --git a/docs/content/developer/iota-evm/explanations/smart-contracts.md b/docs/content/developer/iota-evm/explanations/smart-contracts.md deleted file mode 100644 index 697141f2c72..00000000000 --- a/docs/content/developer/iota-evm/explanations/smart-contracts.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -description: Smart contracts are applications you can trust that run on a distributed network with multiple validators all executing and validating the same code. -image: /img/banner/banner_wasp_core_concepts_smart_contracts.png -tags: - - explanation - - evm ---- - -# Smart Contracts - -![Wasp Node Smart Contracts](/img/banner/banner_wasp_core_concepts_smart_contracts.png) - -## What Are Smart Contracts? - -Smart contracts are software applications that run on a distributed network with multiple validators executing and -validating the same code. This ensures the application behaves as expected and that there is no tampering in the -program's execution. - -### Applications You Can Trust - -As you can be certain that the executed code is always the same (and will not change), this results in -applications you can trust. This allows you to use smart contracts for applications with a trust issue. The -smart contract rules define what the contract can and can not do, making it a decentralized and predictable -decision-maker. - -You can use smart contracts for all kinds of purposes. A recurring reason to use a smart contract is to automate -specific -actions without needing a centralized entity to enforce this specific action. A good example is a smart contract -that can exchange a certain amount of IOTA tokens for land ownership. The smart contract will accept -both the IOTA tokens and the land ownership, and will predictably exchange them between both parties without the risk of -one of the parties not delivering on their promise. **With a smart contract, code is law**. - -### Scalable Smart Contracts - -Anyone willing to pay the fees for deploying a smart contract on a public blockchain can deploy one. Once your smart -contract has been deployed to the chain, you no longer have the option to change it, and you can be sure that your -smart contract application will be there as long as that blockchain exists. Smart contracts can communicate with one -another, and you can invoke programmed public functions on a smart contract to trigger actions on a smart contract, or -address the state of a smart contract. - -Because smart contracts do not run on a single computer but on many validators, a network of validators can only -process so many smart contracts at once, even if the software has been written optimally. This means smart contracts are -expensive to execute, and do not scale well on a single blockchain, often resulting in congested networks and costly -fees for executing functions on smart contracts. **By allowing many blockchains executing smart contracts to run in -parallel** and communicate with one another, **IOTA Smart Contracts solves this scalability problem.** - -At the same time, ISC provides advanced means of communication between its chains and preserves the ability to create -complex, composed smart contracts. diff --git a/docs/content/developer/iota-evm/explanations/state_manager.md b/docs/content/developer/iota-evm/explanations/state_manager.md deleted file mode 100644 index 2dbe85daef9..00000000000 --- a/docs/content/developer/iota-evm/explanations/state_manager.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -description: State manager is Wasp component, which is responsible for keeping the store up to date. -image: /img/logo/WASP_logo_dark.png -tags: - - pruning - - snapshot - - evm ---- - -# State Manager - -State manager aims at keeping the state of the node up to date by retrieving missing data and ensuring that it is -consistently stored in the DB. It services requests by other Wasp components (consensus, _mempool_), which mainly -consist of ensuring that the required state is available in the node: that it may be retrieved from the permanent -store of the node (the database; DB). An obvious way to obtain the latest state is to obtain all of the blocks, -that resulted in making that state. So to obtain state index `n`, state manager first must commit block index `0` -(origin block), then block index `1`, then block index `2` etc. up to block index `n` precisely in that order. -There are two ways for state manager to obtain blocks (other than origin block): - -1. Receive them directly from this node's consensus when the new state[^1] is decided. State manager has no influence - to this process. -2. Receive them from neighbouring nodes upon request, provided the block is available there. - -Independently of the way the block is received, it is stored in state manager's cache (for quicker access) and WAL -(to ensure availability). Therefore it may happen that the block can be retrieved from there. - -[^1] A block is a difference between two consecutive states. To make state index `n`, block index `n` must be obtained -and committed on top of state index `n-1`. Although state manager manipulates blocks, in this description sometimes -"state" and "block" will be used interchangeably as "obtaining block" or "committing block" is essentially the same as -"obtaining state" or "committing state" respectively, having in mind that previous state is already obtained or committed. Block -and state has some other common properties, e.g. block index `n`, which applied to state index `n-1` produces state index `n`, -contains the same commitment as state index `n`. - -## Snapshot - -Once in a while there might be a need to add a new node to the network. This node has no knowledge of chain's history -and it still needs to have the newest state of the chain (to catch up the chain). If the chain has been running for a while, -it might have gone through many sate transitions and downloading that many blocks may take a long period of time. To avoid that, -some nodes in the network can be configured to dump a complete state of the chain at some time into a file periodically -(see `snapshots.period` parameter). This file is called a snapshot. Loading a snapshot to DB produces the same state as downloading -and committing all the blocks that produced that state. However as those blocks aren't downloaded, they are not available in the DB, -except a block with the same state index as snapshot. - -The snapshot format is as follows: - -1. number `4` in 4 byte unsigned integer little endian format representing the length of state index, -2. state index in 4 byte unsigned integer little endian format, -3. number `40` in 4 byte unsigned integer little endian format representing the length of state commitment: `20` bytes for trie root and - `20` bytes for hash of a block, which was last committed to make this state (block with the same index as state), -4. trie root in `20` bytes, -5. the mentioned block's hash in `20` bytes, -6. number `0` in 1 byte unsigned integer format representing snapshot version, -7. bytes representing mentioned block, -8. bytes representing trie of the state. - -The node that makes a snapshot can serve it over http and new nodes can use this to speed up the catch up. Serving the snapshots -over http is beyond the scope of Wasp and should be done in addition. Wasp is only responsible for making snapshots in local -(configurable by `snapshots.localPath` parameter) folder and obtaining them on start when needed from the same local folder or from -configured (by `snapshots.networkPaths` parameter) URLs. A folder, referenced in the `snapshots.networkPaths` parameter must contain -`INDEX` file with new line separated list of snapshot file names. - -If a chain starts with an empty database (usually if the database hasn't been created yet or was deleted), the node checks if -it can load a snapshot: it scans the local folder and all the network addresses for available snapshot files. In the local folder, it reads -all the files with names that satisfy the search pattern `*-*.snap`. In each network location, Wasp reads all the files listed in `INDEX` -file of that location. Wasp reads a state index and a commitment from the contents of these files. File names are not used to obtain -this information, and full snapshot files are not (down)loaded yet. The node chooses -the one with the largest state index and loads it to the store among all available snapshot files. If several files have the same largest state index, -the node loads them one by one, starting from the local ones until one snapshot is loaded correctly. If loading fails for all candidates, -the node will start with an empty database. - -You can use the `snapshots.snapshotsToLoad` parameter to load a specific snapshot. In that case, the node searches for snapshots with -the block hash provided in the parameter. Once again, if loading all found files fails, the node starts with an empty database. - -After a snapshot is loaded, earlier blocks (ones with a smaller state index than the snapshot) cannot be retrieved and committed to the DB -(this is discussed in [Obtaining blocks section](#obtaining-blocks)). This constraint can cause problems (especially in reorg) -if the loaded snapshot is too recent. To avoid that, making snapshots is delayed by `snapshots.delay` states. E.g., if `snapshots.period` -is `100` and `snapshots.delay` is `20`, then snapshot index `100` will be produced. When block index `120` is committed, snapshot index -`200` will be produced, when snapshot index `220` is committed, etc... For the data to be available after this delay, `snapshot.delay` -value must be considerably smaller than `stateManager.pruningMinStatesToKeep`. - -## Obtaining blocks - -Requests to the state manager contain the state commitment and the state manager must ensure, that block (state) with this -commitment is present in the DB. It is possible that to satisfy the request state manager needs to retrieve -several blocks. However this cannot be done in one step as only the commitment of the requested block is known. For this -reason state (block) contains a commitment of the previous block. Previous block must be committed prior to committing the -requested block. And this logic can be extended up to the block, which is already present in the DB, or until the origin -state is reached. - -E.g., let's say, that the last state in the DB is state index `10` and request to have state index `12` is received. -State manager does this in following steps: - -1. Block index `12` is obtained, and commitment of block index `11` is known. -2. As the commitment of block (state) index `11` is known, the block may be requested and obtained. After obtaining block - index `11` commitment of block index `10` is known. -3. Using block index `10` commitment the DB is checked to make sure that it is already present. -4. As block index `10` is already committed, block index `11` is committed. This makes state `11` present in the DB. -5. As state `11` is already committed, block index `12` is committed. This makes state `12` present in the DB and completes - the request. - -To obtain blocks, state manager sends requests to 5 other randomly chosen nodes. If the block is not received (either messages -got lost or these nodes do not have the requested block), 5 other randomly chosen nodes are queried. This process is repeated -until the block is received (usually from other node but may also be from this node's consensus) or the request is no longer -valid. - -If difference between state indexes of requested state and available in the DB state is large, this chain can get very long. -In order to limit its length, if requested block index is a) smaller than state index of snapshot, which was loaded on node -start, or b) smaller than largest state index among the pruned blocks (see [Pruning](#pruning)), the node panics. If this panicking -continues, the administrator may decide to delete the DB and start the node from (possibly configured) snapshot. - -## Block cache - -Block cache is in memory block storage. It keeps a limited amount (configured by `stateManager.blockCacheMaxSize`) of blocks -for limited amount of time (configured by `stateManager.blockCacheBlocksInCacheDuration`) to make the retrieval -quicker. E.g., in the last step of example of the previous section block index `12` must be committed. It is obtained in -the step 1, but as several steps of the algorithm are spread over time with requests to other nodes in between, and -several requests to obtain the same block may be present, it is not feasible to store it in request. However it would -be wasteful to fetch it twice on the same request. So the block is stored in cache in step 1 of the algorithm and -retrieved from cache later in the last step. - -The block is kept in the cache no longer that predetermined amount of time (configured by `stateManager.blockCacheBlocksInCacheDuration`). -If upon writing to cache blocks in cache limit is exceeded, block, which is in cache the longest, is removed from cache. - -## Block write ahead log (WAL) - -Upon receiving a block, its contents is dumped into a file and stored in a file system. The set of such files is WAL. - -The primary motivation behind creating it was in order not to deadlock the chain. Upon deciding on next state committee -nodes send the newest block to state manager and at the same time one of the nodes send the newest transaction to L1. -In an unfavourable chain of events it might happen that state managers of the committee nodes are not fast enough to commit -the block to the DB (see algorithm in [Obtaining blocks section](#obtaining-blocks)), before the node crashes. This leaves -the nodes in the old state as none of the nodes had time to commit the block. However the L1 reports the new state as -the latest although none of the nodes can be transferred to it. The solution is to put the block into WAL as soon as -possible so it won't be lost. - -Currently upon receiving the new confirmed block from node's consensus, state manager is sure that its predecessor is in the DB, -because consensus sends other requests before sending the new block, so WAL isn't that crucial any more. However, it is useful -in several occasions: - -1. Storing preliminary block, which is sent by consensus of other nodes. -2. When the node is catching up many states and block cache limit is too small to store all the blocks, WAL is used to avoid - fetching the same block twice. -3. In case of adding new node to the network to avoid catch up taking a lot of time when snapshots are not available, - the new node can be configured (`wal.loadToStore=true`) to load the DB with blocks from WAL on startup. WAL can be copied - from some other node. This is also true for any catch up over many states. - -## Pruning - -In order to limit the DB size, old states are deleted (pruned) from it on a regular basis. The amount of states to keep is -configured by two parameters: one in the configuration of the node (`stateManager.pruningMinStatesToKeep`) and one in the governance contract -(`BlockKeepAmount`). The resulting limit of previous states to keep is the larger of the two. Every time a block is committed -to the DB, states which are over the limit are pruned. However, to avoid freezing State manager for too long, no more than -`stateManager.pruningMaxStatesToDelete` blocks are pruned in a single run. The algorithm ensures that oldest states are pruned -first to avoid gaps between available states on the event of some failure. - -Pruning may be disabled completely via node configuration to make an archive node: node that contains all the state ever -obtained by the chain. Note, that such node will require a lot of resources to maintain: mainly disk storage. - -## Parameters - -### State manager - -The following parameters may be provided in section `stateManager`: - -- `blockCacheMaxSize`: the limit of the blocks in block cache. Default is 1k. -- `blockCacheBlocksInCacheDuration`: the limit of the time block stays in block cache. Default is 1 hour. -- `blockCacheBlockCleaningPeriod`: how often state manager should find and delete blocks, that stayed in block cache - for too long. Default is every minute. -- `stateManagerGetBlockRetry`: how often requests to retrieve the needed blocks from other nodes should be repeated. - Default is every 3 seconds. -- `stateManagerRequestCleaningPeriod`: how often state manager should find and delete requests, that are no longer valid. - Default is every second. -- `stateManagerTimerTickPeriod`: how often state manager should check if some maintenance (cleaning requests or block cache, - resending requests for blocks) is needed. Default is every second. There is no point in making this value larger than - any of `blockCacheBlockCleaningPeriod`, `stateManagerGetBlockRetry` or `stateManagerRequestCleaningPeriod`. -- `pruningMinStatesToKeep` : minimum number of old states to keep in the DB. Note that if `BlockKeepAmount` in - governance contract is larger than this value, then more old states will be kept. - Default is 10k. 0 (and below) disables pruning. -- `pruningMaxStatesToDelete`: maximum number of states to prune in one run. This is needed in order not to grab - state manager's time for pruning for too long. Default is 1k. - -### Snapshots - -The following parameters may be provided in section `snapshots`: - -- `snapshotsToLoad`: the comma sepparated list of `:` pairs, where chain `` must be started using snapshot with block hash ``. - The list can also contain `` entry. This hash will be used for other chains, which are not configured separately. There is - no point in having several `` or `:` entries with the same `` as only the last such entry is taken into - account. Note that if the chain is configured to start from some snapshot and the snapshot is not available (or another error occurs - during snapshot loading), the chain will start with an empty DB. The default is an empty list, which means that the newest available snapshot - will be loaded for every chain. -- `period`: how often state snapshots should be made: 1000 meaning "every 1000th state", 0 meaning "making snapshots is disabled". - Snapshots are disabled by default. -- `delay`: how many states to delay making the snapshot; it must be considerably smaller than `stateManager.pruningMinStatesToKeep`. - The default is 20. -- `localPath`: the path to the snapshots folder in this node's disk. Default is `waspdb/snap`. -- `networkPaths`: the comma-separated list of URLs that serve snapshots. The URLs may have the HTTP (e.g., `http://server.org/path/`) or the HTTPS - (e.g., `https://server.org/path/`) scheme for remote locations or a file path (e.g., `file://path/to/folder`) scheme for local snapshot locations. - The scheme is compulsory in the URL. The list is empty by default. - -### WAL - -The following parameters may be provided in section `wal`: - -- `loadToStore`: load blocks from WAL to the store on node start-up. This function is off (`false`) by default. -- `enabled`: whether the WAL is enabled. It is enabled by default. -- `path`: the path to the WAL folder. Default is `waspdb/wal`. diff --git a/docs/content/developer/iota-evm/explanations/states.md b/docs/content/developer/iota-evm/explanations/states.md deleted file mode 100644 index e018cd413b9..00000000000 --- a/docs/content/developer/iota-evm/explanations/states.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -description: The state of the chain consists of balances of native IOTA digital assets and a collection of key/value pairs which represents use case-specific data stored in the chain by its smart contracts outside the UTXO ledger. -image: /img/iota-evm/chain0.png -tags: - - explanation - - state - - evm ---- - -# State, Transitions, and State Anchoring - -## State of the Chain - -The state of the chain consists of: - -- A ledger of accounts owning IOTA _digital assets_ (base tokens, native tokens, and NFTs). The chain acts as a custodian - for those funds on behalf of each account's owner. -- A collection of arbitrary key/value pairs (the _data state_) that contains use case-specific data stored by the smart - contracts in the chain. - -The chain's state is an append-only (immutable) _data structure_ maintained by the distributed consensus of its -validators. - -## Digital Assets on the Chain - -Each native L1 account in the IOTA UTXO ledger is represented by an address and controlled by an entity holding the -corresponding private/public key pair. -In the UTXO ledger, an account is a collection of UTXOs belonging to the address. - -Each ISC L2 chain has a L1 account, called the _chain account_, holding all tokens entrusted to the chain in a single -UTXO, the _state output_. -It is similar to how a bank holds all deposits in its vault. This way, the chain (the entity controlling the state -output) becomes a custodian for the assets owned by its clients, similar to how the bank’s client owns the money -deposited in the bank. - -The consolidated assets held in the chain are the _total assets on-chain_, which are contained in the state output of -the chain. - -The chain account is controlled by a _chain address_, also known as _chain ID_. -It is a special kind of L1 address, an _alias address_, which abstracts the controlling entity (the state controller -address) from the identity of the chain: the controlling entity of the chain may change, while the chain ID stays the -same. - -## The Data State - -The data state of the chain consists of a collection of key/value pairs. -Each key and each value are arbitrary byte arrays. - -In its persistent form, the data state is stored in a key/value database outside the UTXO ledger and maintained by the -validator nodes of the chain. -The state stored in the database is called the _solid state_. - -While a smart contract request is being executed, the _virtual state_ is an in-memory collection of key/value pairs that -can become solid upon being committed to the database. -An essential property of the virtual state is the possibility of having several virtual states in parallel as -candidates, with a possibility for one of them to be solidified. - -The data state has a state hash, a timestamp, and a state index. -The state hash is usually a Merkle root, but it can be any hashing function of all data in the data state. - -The data state hash and on-chain assets are contained in a single atomic unit on the L1 ledger: the state UTXO. -Each state mutation (state transition) of the chain is an atomic event that changes the on-chain assets and the data -state, consuming the previous state UTXO and producing a new one. - -## Anchoring the State - -The data state is stored outside the ledger, on the distributed database maintained by _validator_ nodes. -_Anchoring the state_ means placing the hash of the data state into the state UTXO and adding it to the L1 UTXO ledger. -The UTXO ledger guarantees that there is _exactly one_ such output for each chain on the ledger at every moment. -We call this output the _state output_ (or state anchor) and the containing transaction the _state transaction_ (or -anchor transaction) of the chain. -The state output is controlled (i.e., can be unlocked/consumed) by the entity running the chain. - -With the anchoring mechanism, the UTXO ledger provides the following guarantees to the IOTA Smart Contracts chain: - -- There is a global consensus on the state of the chain -- The state is immutable and tamper-proof -- The state is consistent (see below) - -The state output contains: - -- The identity of the chain (its L1 alias address) -- The hash of the data state -- The state index, which is incremented with each new state output - -## State Transitions - -The data state is updated by mutations of its key/value pairs. -Each mutation either sets a value for a key or deletes a key (and the associated value). -Any update to the data state can be reduced to a partially ordered sequence of mutations. - -A _block_ is a collection of mutations to the data state that is applied in a state transition: - -```go -next data state = apply(current data state, block) -``` - -The state transition in the chain occurs atomically in a L1 transaction that consumes the previous state UTXO and -produces the next one. The transaction includes the movement of the chain's assets and the update of the state hash, - -At any moment in time, the data state of the chain is a result of applying the historical sequence of blocks, starting -from the empty data state. - -![State transitions](/img/iota-evm/chain0.png) - -On the L1 UTXO ledger, the state's history is represented as a sequence (chain) of UTXOs, each holding the chain’s -assets in a particular state and the anchoring hash of the data state. -Note that not all the state's transition history may be available: due to practical reasons, older transactions may be -pruned in a snapshot process. -The only thing guaranteed is that the tip of the chain of UTXOs is always available (which includes the latest data -state hash). - -The ISC virtual machine (VM) computes the blocks and state outputs that anchor the state, which ensures that the state -transitions are calculated deterministically and consistently. - -![Chain](/img/iota-evm/chain1.png) diff --git a/docs/content/developer/iota-evm/explanations/validators.md b/docs/content/developer/iota-evm/explanations/validators.md deleted file mode 100644 index 157713ef4df..00000000000 --- a/docs/content/developer/iota-evm/explanations/validators.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -description: Each chain is run by a network of validator nodes which run a consensus on the chain state update. -image: /img/logo/WASP_logo_dark.png -tags: - - validator - - access-node - - consensus - - explanation - - evm ---- - -# Validators - -Each chain is run by that chain's _committee of validators_. This committee owns a key that is split between all of its -validators. Each key share is useless on its own, but a collective signature gives validators complete control over the -chain. - -The committee of validators is responsible for executing the smart contracts in the chain and thus calculating a _state -update_. -All validators execute exactly the same code and reach a consensus on the state update. -Once the next state is computed and validated, it is committed to each validator's database, a new _block_ is added to -the chain (containing the state mutations), and the _state hash_ is saved in the L1 ledger. - -Depending on the governance model, chain owners can rotate the committee of validators. -By rotating the committee of validators, validators can be deleted, added, or replaced. - -ISC does not define how to select validators to form a committee: it could be a solitary choice of the chain's owner, or -it could be a public competition between candidates. -ISC does not define how validators are rewarded either. - -## Access Nodes - -It is possible to have some nodes act as _access nodes_ to the chain without being part of the committee of -validators. -All nodes in the subnet (validators and non-validators) are connected through statically assigned trust -relationships and each node is also connected to the IOTA L1 node to receive updates on the chain’s L1 -account. - -Any node can optionally provide access to smart contracts for external callers, allowing them to: - -- Query the state of the chain (i.e., _view calls_) -- Send off-ledger requests directly to the node (instead of sending an on-ledger request as a L1 transaction) - -It is common for validator nodes to be part of a private subnet and have only a group of access nodes exposed to the -outside world, protecting the committee from external attacks. - -The management of validator and access nodes is done through -the [`governance` core contract](../../../references/iota-evm/core-contracts/governance.md). diff --git a/docs/content/developer/iota-evm/getting-started/compatibility.md b/docs/content/developer/iota-evm/getting-started/compatibility.md deleted file mode 100644 index 1c9dd2125c1..00000000000 --- a/docs/content/developer/iota-evm/getting-started/compatibility.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -description: Compatibility between the ISC EVM layer and existing Ethereum smart contracts and tooling. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - reference ---- - -# EVM Compatibility in IOTA Smart Contracts - -The [`evm`](../../../references/iota-evm/core-contracts/evm.md) [core contract](../../../references/iota-evm/core-contracts/overview.md) -provides EVM support in IOTA Smart Contracts. It stores the EVM state (account balances, state, code, -etc.) and provides a way to execute EVM code to manipulate the state. - -The EVM core contract runs on top of the ISC layer, which provides the rest of the machinery needed to run smart -contracts, such as signed requests, blocks, state, proofs, etc. - -However, the ISC EVM layer is also designed to be as compatible as possible with existing Ethereum tools -like [MetaMask](https://metamask.io/), which assume that the EVM code runs on an Ethereum blockchain composed of -Ethereum blocks containing Ethereum transactions. Since ISC works in a fundamentally different way, -providing 100% compatibility is not possible. We do our best to emulate the behavior of an Ethereum node, so the -Ethereum tools think they are interfacing with an actual Ethereum node, but some differences in behavior are inevitable. - -## Properties and Limitations - -Here are some of the most important properties and limitations of EVM support in IOTA Smart Contracts: - -### No Enforced Block Time - -There is no guaranteed _block time_. A new EVM "block" will be created only when an ISC block is created, and ISC does -not enforce an average block time. This means that block times are variable; a new block will be created as soon as needed. - -### The Magic Contract - -A [dedicated Ethereum contract](../how-tos/core-contracts/introduction.md) exists to manage Layer 1 tokens and ISC -functionalities, introducing commands like `isc.send(...)` for token transfers. - -### Gas Fees - -Gas fees are set by the chain. diff --git a/docs/content/developer/iota-evm/getting-started/languages-and-vms.mdx b/docs/content/developer/iota-evm/getting-started/languages-and-vms.mdx deleted file mode 100644 index e9447352c63..00000000000 --- a/docs/content/developer/iota-evm/getting-started/languages-and-vms.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -description: Compatibility between languages and different virtual machines. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - reference ---- - -import EVMCompatibility from '../../../_snippets/iota-evm/EVM_compatibility.md'; - -# Supported Virtual Machines & Languages - -The current release of IOTA Smart Contracts has support for [EVM/Solidity](#what-is-evmsolidity) smart -contracts, as well as experimental [Wasm](#wasm-vm-for-isc) smart contracts, providing compatibility with -existing smart contracts and tooling from other EVM based chains like Ethereum. This allows us to offer the existing -ecosystem around EVM/Solidity a familiar alternative. - -## EVM Smart Contracts - -### What is EVM/Solidity? - -[EVM](https://ethereum.org/en/developers/docs/evm/) stands for "Ethereum Virtual Machine" and is currently the tried and -tested virtual machine running most smart contract networks. - -[Solidity](https://soliditylang.org/) is the programming language of choice for the EVM. It was created for this -specific purpose. - -The main benefit of using EVM/Solidity is its sheer amount of resources from years of development. The IOTA -Smart Contracts implementation is fully compatible with all of them. If you have experience developing on other EVM based chains, you will feel right at home. Any existing contracts you've written will need no -changes to function on IOTA Smart Contracts. - -### How IOTA Smart Contracts Work With EVM - -Every deployed ISC chain automatically includes a core contract -called [`evm`](../../../references/iota-evm/core-contracts/evm.md). This core contract is responsible for running EVM code and -storing the EVM state. - -The Wasp node also provides a standard JSON-RPC service, which allows you to interact with the EVM layer using existing -tooling like [MetaMask](https://metamask.io/), [Remix](https://remix.ethereum.org/) or [Hardhat](https://hardhat.org/). -Deploying EVM contracts is as easy as pointing your tools to the JSON-RPC endpoint. - - - -## Wasm VM for ISC - -:::warning Experimental - -The Wasm _VM_ is in experimental state, showcasing ISC's "VM plugin" architecture. - -Experiment but avoid using it for production applications; opt for [EVM](quick-start.mdx). - -::: - -IOTA Smart Contracts (ISC) provide a sandboxed environment through an API, facilitating secure and deterministic -interactions with ISC functions. This API supports any Virtual Machine (VM) aiming to build a system for smart contract -code execution on ISC. - -![Wasp node ISC Host](/img/iota-evm/wasm_vm/IscHost.png) - -You can use a [WebAssembly (Wasm)](https://webassembly.org/) VM as a compilation target, facilitated by the open-source -[Wasmtime runtime](https://wasmtime.dev/). This setup encourages dynamic smart contract operations compiled to Wasm code, -promoting security and adaptability with different programming languages. - -![Wasm VM](/img/iota-evm/wasm_vm/WasmVM.png) - -The Wasm VM operates with self-contained `WasmLib` libraries linked to individual Wasm codes, optimizing the ISC sandbox -functionality and smart contract state storage access. - -### Supported Functionalities - -The ISC sandbox environment offers: - -- Smart contract metadata and state data access. -- Request data retrieval for function calls. -- Token management within the contract. -- Utility functions from the host. -- Smooth initiation of other smart contract functions. -- Logging facility. - -### Supported Languages - -The WasmLib started with [Rust](https://www.rust-lang.org/) support, expanding to include [Go](https://golang.org/) -and [TypeScript](https://www.typescriptlang.org/) with the help of respective Wasm code generators: - -| Language | Wasm code generator | -| ---------- | -------------------------------------------------- | -| Go | [TinyGo](https://tinygo.org/) | -| Rust | [wasm-pack](https://rustwasm.github.io/wasm-pack/) | -| TypeScript | [AssemblyScript](https://www.assemblyscript.org/) | - -These generators maintain a common subset of their host language, aiming for a unified coding style to simplify the -initiation into smart contract creation, welcoming developers with a C-style language background to quickly adapt. diff --git a/docs/content/developer/iota-evm/getting-started/networks-and-chains.mdx b/docs/content/developer/iota-evm/getting-started/networks-and-chains.mdx deleted file mode 100644 index a700a42dc80..00000000000 --- a/docs/content/developer/iota-evm/getting-started/networks-and-chains.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -description: Networks and endpoints in the IOTA ecosystem. -tags: - - mainnet - - testnet - - reference - - evm ---- - -import { AddToMetaMaskButton } from '@site/src/components/AddToMetaMaskButton'; -import { Networks } from '@site/src/components/constant'; -import NetworkInfo from '@site/src/components/NetworkInfo'; - -# Networks & Chains - -## IOTA EVM Testnet - - - -[The IOTA EVM Testnet](https://explorer.evm.testnet.iotaledger.net/) runs as a layer 2 on top -of the IOTA Testnet. This network uses ISC to facilitate -an Ethereum Virtual Machine and has an enshrined bridge to layer 1. - -:::info - -This network is subject to occasional resets (no data retention) which are usually announced with a one-week grace period. - -::: - - - -:::note - -The other values (network name and currency symbol) can be whatever value you like. - -::: - -## IOTA EVM - -[IOTA EVM](https://explorer.evm.iota.org) is the L2 EVM running on top of the IOTA network. - - - - - -### Additional Info - - - -## IOTA EVM Testnet - - - -[The IOTA EVM Testnet](https://explorer.evm.testnet.iotaledger.net/) runs as a layer 2 on top -of the IOTA Testnet. This network uses ISC to facilitate - -:::info - -This network is subject to occasional resets (no data retention) which are usually announced with a one-week grace period. - -::: - - - -:::note - -The other values (network name and currency symbol) can be whatever value you like. - -::: - -### Additional Info - - - -## ShimmerEVM Testnet - - - -[The ShimmerEVM Testnet](https://explorer.evm.testnet.shimmer.network/) runs as a layer 2 on top -of the Shimmer Testnet. This network uses ISC to facilitate -an Ethereum Virtual Machine and has an enshrined bridge to layer 1. - -:::info - -This network is subject to occasional resets (no data retention) which are usually announced with a one-week grace period. - -::: - - - -:::note - -The other values (network name and currency symbol) can be whatever value you like. - -::: - -### Additional Info - - - -## ShimmerEVM - -[ShimmerEVM](https://explorer.evm.shimmer.network/) is the L2 EVM running on top of the Shimmer network. - - - - - -### Additional Info - - - -## ShimmerEVM Testnet - - - -[The ShimmerEVM Testnet](https://explorer.evm.testnet.shimmer.network/) runs as a layer 2 on top -of the Shimmer Testnet. This network uses ISC to facilitate -an Ethereum Virtual Machine and has an enshrined bridge to layer 1. - -:::info - -This network is subject to occasional resets (no data retention) which are usually announced with a one-week grace period. - -::: - - - -:::note - -The other values (network name and currency symbol) can be whatever value you like. - -::: - -### Additional Info - - - -## Core Contracts - -[IOTA EVM](#iota-evm), [ShimmerEVM](#shimmerevm) and the testnet networks have 7 -[core contracts](../../../references/iota-evm/core-contracts/overview.md) deployed, as well as the -[Magic Contract](../../../references/iota-evm/magic-contract/introduction.md). diff --git a/docs/content/developer/iota-evm/getting-started/quick-start.mdx b/docs/content/developer/iota-evm/getting-started/quick-start.mdx deleted file mode 100644 index 5e7856a723b..00000000000 --- a/docs/content/developer/iota-evm/getting-started/quick-start.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -description: This guide will help you quickly get started with the EVM -image: /img/logo/WASP_logo_dark.png -tags: - - quickstart - - evm - - solidity - - rpc ---- - -import DeployAdmonition from '../../../_snippets/iota-evm/deploy_a_smart_contract.md'; -import MetamaskButtons from '../../../_snippets/iota-evm/metamask_buttons.mdx'; -import { Networks } from '@site/src/components/constant'; -import Link from '@docusaurus/Link'; - -# EVM Quickstart Guide - -This guide will help you quickly get started with our EVM, where you can deploy and interact with EVM-compatible smart contracts. - -## Prerequisites - -- [MetaMask](https://metamask.io/) browser extension installed - -## Setup MetaMask - -Click this button: - - - -:::tip - -Please read [the MetaMask section in the tools guide](tools.mdx#metamask) for a detailed guide. - -::: - -## Deploy and Interact with Smart Contracts - -:::tip Fund your testnet account - -If you are using one of the testnets you can just use the the toolkit to get testnet tokens. - -1. Go to the [IOTA EVM](https://evm-toolkit.evm.testnet.iotaledger.net) or [ShimmerEVM](https://evm-toolkit.evm.testnet.shimmer.network/) Testnet Toolkit. -2. Connect your MetaMask wallet by clicking "Connect Wallet" or paste an EVM address. -3. Select the account you want to receive testnet tokens. -4. Click "Send funds" to get testnet tokens. - -::: - -You can now deploy and interact with smart contracts. Utilize popular development tools and frameworks, such as [Hardhat](https://hardhat.org/), or [Remix](https://remix.ethereum.org/), to build, test, and deploy your smart contracts. - - - -## Explore the Network - -Visit the corresponding Block Explorer to monitor the chain, track transactions, and explore deployed smart contracts. - - - - Explorer - - - Explorer - - - Explorer - - - Explorer - - - -## Additional Resources - -- [GitHub issues page for Wasp](https://github.com/iotaledger/wasp/issues) -- [Firefly](https://firefly.iota.org) - -With this quickstart guide, you should now be able to set up and start exploring the EVM. As you begin to deploy and interact with smart contracts, remember to provide feedback on any issues or improvements you discover to help make our EVM even better. Happy developing! diff --git a/docs/content/developer/iota-evm/getting-started/tools.mdx b/docs/content/developer/iota-evm/getting-started/tools.mdx deleted file mode 100644 index 89562e5a393..00000000000 --- a/docs/content/developer/iota-evm/getting-started/tools.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -description: Existing EVM tooling is compatible and can be used directly with an IOTA Smart Contracts chain running EVM. You can configure hardhat, metamask, remix, Ether.js and Web3.js among others. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - cli - - rpc - - reference ---- - -import { AddToMetaMaskButton } from '@site/src/components/AddToMetaMaskButton'; -import HardhatConfig from '../../../_snippets/iota-evm/hardhat_config.mdx'; -import { Networks } from '@site/src/components/constant'; -import DeployAdmonition from '../../../_snippets/iota-evm/deploy_a_smart_contract.md'; -import { ChainId } from '@site/src/components/ChainId'; -import NetworkInfo from '@site/src/components/NetworkInfo'; -import OraclesContractData from '../../../_snippets/iota-evm/oracles_contract_data.mdx'; - -# Compatible Tools - -EVM on IOTA Smart Contracts has been integrated in a way that the existing EVM tooling is compatible and can be used -directly with an IOTA Smart Contracts chain running EVM as long as you take a couple of things into account. - -## Tooling Considerations - -1. Please make sure you use the correct JSON-RPC endpoint URL in your tooling for your chain. If you're running your own chain, you can find the JSON-RPC - endpoint URL in the Wasp dashboard (`[URL]/wasp/dashboard` when using `node-docker-setup`). -2. Please ensure you use the correct `Chain ID` configured while starting the JSON-RPC service. If you did not explicitly define this while starting the service, the default Chain ID will be - for IOTA EVM, - for ShimmerEVM or for the EVM Testnet. -3. Fees are handled on the IOTA Smart Contracts chain level, not the EVM level. The chain will reject any requests with a different gas price than specified by the chain. - -:::caution - -Re-using an existing Chain ID is not recommended and can be a security risk. For production usage, register a unique Chain -ID on [Chainlist](https://chainlist.org/) and use that instead of the default. **It is not possible to change the EVM -chain ID after deployment.** - -::: - -## Network RPCs - - - - - - - - - - - - - - - - - - - - -## IOTA EVM Tools - -The following tools are **only available on IOTA EVM**. - -### Blast API - -The Blast API is a decentralized platform that provides reliable and scalable node infrastructure -for accessing blockchain data. You can find the Blast API URLs in the [Network RPCs](#network-rpcs) - -### EVM Toolkit - -You can use the [IOTA EVM Toolkit](https://evm-toolkit.evm.iotaledger.net) to withdraw assets from IOTA EVM to IOTA L1. -It also includes a wrapper wIOTA, which wraps IOTA. - -### Multicall3 - -If you need to aggregate results from multiple contract reads into a single JSON-RPC request or execute multiple -state-changing calls in a single transaction, you can use the [Multicall3 contract](https://explorer.evm.iota.org/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract). - -## IOTA EVM and ShimmerEVM Tools - -The following tools are available on both IOTA EVM end ShimmerEVM. - -### MultiSig Wallets - -If you require and additional level of security, you can use the [Safe{} Wallet](https://safe.iotaledger.net/) as a -Multisig solution on IOTA EVM. - -### Oracles - -If your project requires Oracles to provide data from the outside world, you find both Pyth and Supra have integrated IOTA EVM. - - - -### Subgraphs - -Subgraphs provide a streamlined way for developers to access blockchain data relevant to their applications, -significantly enhancing developer efficiency and user experience. IOTA EVM subgraphs available via [Goldsky](https://goldsky.com). - -## MetaMask - -[MetaMask](https://metamask.io/) is a popular EVM wallet which runs in a browser extension that allows you to -interact with EVM chains and their applications (dApps). - -To use your EVM chain with MetaMask, simply open up MetaMask and click on the network drop-down list at the very top. At -the bottom of this list, you will see the option `Add network`. On the new page you will see a list of popular network with the option `Add a network manually`. -For example this would be the configs to add our different EVM chains: - - - - - - - - - - - - - - - - - - - - -Ensure that your `RPC Url` and `Chain ID` are set correctly and match the dashboard values. The `Network Name` can be -whatever you see fit. Click "Save" to add the chain to MetaMask. - -If you wish to use additional EVM chains with Metamask, you can add more Custom RPC networks, as long as they have a -unique `Chain ID` and `RPC Url`. Once you have done this, you can start using MetaMask to manage your EVM wallet or -issue/sign transactions with any dApp running on that network. - -### Remix - -If you also want to use the [Remix IDE](https://remix.ethereum.org/) to deploy any regular Solidity Smart Contract, you -should set the environment as **Injected Provider - Metamask**, which should then connect with your MetaMask wallet. - -Click on the _Deploy & Run transactions_ button in the menu on the left and select `Injected Web3` from -the `Environment` dropdown. - -![Select Injected Provider from the Environment dropdown](/img/iota-evm/evm/remix-injected-provider-metamask.png) - - - -Metamask will ask to connect to Remix, and once connected, it will set the `Environment` to `Injected Web3` with -the "Custom (Chain ID) network". - -![Environment will be set to Injected Web3](/img/iota-evm/evm/remix-injected-provider-set.png)] - -## Hardhat - -[Hardhat](https://hardhat.org/) is a command line toolbox that allows you to deploy, test, verify, and interact with -Solidity smart contracts on an EVM chain. EVM chains running on IOTA Smart Contracts are compatible with Hardhat; simply -make sure you add the correct network parameters to your `hardhat.config.js`, for example: - - - - - -:::caution - -Currently, there is no validation service available for EVM/Solidity smart contracts on IOTA Smart Contracts, which is -often offered through block explorer APIs. - -::: - -## Ethers.js/Web3.js - -If you input the correct configuration parameters for the JSON-RPC endpoint to talk to, -[Ethers.js](https://docs.ethers.io/) and [Web3.js](https://web3js.readthedocs.io/) are also compatible with EVM chains on IOTA Smart Contracts. -Alternatively, you can let both interact through MetaMask instead so that it uses the -network configured in MetaMask. For more information on this, read their documentation. - -## Other Tooling - -Most tools available will be compatible if you enter the correct `Chain ID` and `RPC Url`. diff --git a/docs/content/developer/iota-evm/how-tos/ERC20.mdx b/docs/content/developer/iota-evm/how-tos/ERC20.mdx deleted file mode 100644 index ebe8e136752..00000000000 --- a/docs/content/developer/iota-evm/how-tos/ERC20.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -description: Solidity smart contract ERC20. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - how-to - - erc20 ---- - -import DeployAdmonition from '../../../_snippets/iota-evm/deploy_a_smart_contract.md'; -import PriorKnowledge from '../../../_snippets/iota-evm/EVM-required-prior-knowledge.md'; -import RemixIDE from '../../../_snippets/iota-evm/remix-IDE.md'; - -# Create ERC20 Custom Tokens - -## Required Prior Knowledge - - - -## About ERC20 - -ERC20 is a standard for fungible tokens and is defined in -the [EIP-20 Token Standard](https://eips.ethereum.org/EIPS/eip-20) by Ethereum. - -With the ERC20 standard, you can create your own tokens and transfer them to the EVM on IOTA Smart Contracts without -fees. - - - -## 1. Create the Smart Contract - -Create a new Solidity file, for example, `ERC20.sol` in the contracts folder of -your [Remix IDE](https://remix.ethereum.org/) and add this code snippet: - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.7; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract ExampleERC20Token is ERC20 { - constructor() ERC20("ExampleERC20Token", "EET") { - _mint(msg.sender, 1000000 * 10 ** decimals()); - } -} -``` - -This imports all functions from the [OpenZeppelin ERC20](https://docs.openzeppelin.com/contracts/4.x/erc20) smart -contract and creates a new ERC20 token with your name and Symbol. - -:::tip OpenZeppelin - -OpenZeppelin provides many audited smart contracts and is a good point to start and learn. - -::: - -:::note Customize Your Token - -You can change the token name `ExampleERC20Token` and the token symbol `EET` for anything you want. - -::: - - - -## 2. Add Your Custom Tokens to MetaMask - -Once you have deployed your contract, you can add your new custom token to your Metamask account. - -1. Open Metamask, and click on the transaction that created the contract. From there, you can simply click - on `View on block explorer` to visit the transaction details. Alternatively, you can copy the transaction ID and - visit the [IOTA EVM Explorer](https://explorer.evm.iota.org), - [ShimmerEVM Explorer](https://explorer.evm.testnet.shimmer.network/) - or [EVM Testnet Explorer](https://explorer.evm.testnet.shimmer.network/) and use the search bar to find transaction. - -!['View on block explorer](/img/iota-evm/evm/how-tos/ERC20/metamask-get-transaction-or-go-to-block-explorer.png) - -2. Copy the contract address from the transaction details, and import your custom ERC20 tokens into MetaMask. - -![Copy contract address](/img/iota-evm/evm/how-tos/ERC20/metamask-import-tokens.png) - -## 3. Have some Fun - -Now you should see your token in MetaMask. You can send them to your friends without any fees or gas costs. - -![Copy contract address](/img/iota-evm/evm/how-tos/ERC20/metamask-erc20-balance.png) - -You also can ask in the [Discord Chat Server](https://discord.iota.org) to send them around and discover what the -community is building on IOTA Smart Contracts. diff --git a/docs/content/developer/iota-evm/how-tos/ERC721.mdx b/docs/content/developer/iota-evm/how-tos/ERC721.mdx deleted file mode 100644 index d3c5164003e..00000000000 --- a/docs/content/developer/iota-evm/how-tos/ERC721.mdx +++ /dev/null @@ -1,124 +0,0 @@ ---- -description: Create and deploy a Solidity smart contract to mint NFTs using the ERC721 standard. -image: /img/iota-evm/evm/ozw-721.png -tags: - - evm - - solidity - - how-to - - erc721 ---- - -import DeployAdmonition from '../../../_snippets/iota-evm/deploy_a_smart_contract.md'; -import PriorKnowledge from '../../../_snippets/iota-evm/EVM-required-prior-knowledge.md'; -import RemixIDE from '../../../_snippets/iota-evm/remix-IDE.md'; - -# Create ERC721 NFTs - -:::info EVM-only NFT - -Please keep in mind that this is an EVM-only NFT. It's not tied to L1 native assets. Also, these are different from L1 -NFTs. - -::: - - - -## About ERC721 - -Non-fungible tokens or NFTs are a type of token that can represent any unique object, including a real-world asset on a -decentralized network. NFTs are commonly represented by the ([ERC721 standard](https://eips.ethereum.org/EIPS/eip-721)). -You can use the -openzepplin -lib [`@openzeppelin/contracts/token/ERC721/ERC721.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) -to streamline your development experience. - -You can also use the ([OpenZepplin Contracts Wizard](https://wizard.openzeppelin.com/#erc721)) to generate and customize -your smart contracts. - - - -## Create the Smart Contract - -The following is an example NFT Smart Contract called "IotaEVMSampleNFT". - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts@5.0.1/token/ERC721/ERC721.sol"; -import "@openzeppelin/contracts@5.0.1/access/Ownable.sol"; - -contract IotaEVMSampleNFT is ERC721, Ownable { - uint256 private _nextTokenId; - - constructor(address initialOwner) - ERC721("IotaEVMSampleNFT", "SSNFT") - Ownable(initialOwner) - {} - - function _baseURI() internal pure override returns (string memory) { - return "https://example.com/nft/"; - } - - function safeMint(address to) public onlyOwner { - uint256 tokenId = _nextTokenId++; - _safeMint(to, tokenId); - } -} -``` - -As you can see above, the contract uses standard methods for the most part. You should pay attention to the following: - -- `pragma solidity ^0.8.20;`: This line means the contract uses solidity compiler version `0.8.20` or above. -- `contract IotaEVMSampleNFT is ERC721, ERC721URIStorage, Ownable`: This line defines the contract's name, and what - other contracts it implements. -- `ERC721("IotaEVMSampleNFT", "SNFT") {}`: This line defines the token name and symbol. You can name it - whatever you want. We recommend using the same name for the token and the contract. -- `return "https://example.com/nft/";`: You should define the base URI of your NFTs. That means the URL you provide here - will be used for all your tokens. Since this contract uses auto-incremental token IDs, your token URI will look - something like `https://example.com/nft/0`, `https://example.com/nft/1`, `https://example.com/nft/2`, and so on. -- `function safeMint(address to, string memory uri) public onlyOwner {`: The `safeMint` function will - require that you manually input a token's `to` address and a `uri` every time you want to mint. This should work for - regular use cases. - -### Customize on OpenZeppelin - -You can customize your contract depending on how you want it to behave. You should consider the following topics -and questions: - -1. **Ownership** — Who owns it? How is it stored? -2. **Creation** — Method or Type of Creation. -3. **Transfer & Allowance** — How will tokens be transferred? How will they be available to other addresses and - accounts? -4. **Burn** — Do you want to destroy it? If yes, how? - -You can click on `Copy to Clipboard` and paste it into the IDE of your choice, download it, or click on `Open in Remix` -directly. - -:::note Set the Initial Owner - -Before you can deploy this contract, you will need to set the `Initial Owner` address; this can be your own IOTA EVM address. - -!["Set the initial owner" img.png](/img/iota-evm/evm/how-tos/ERC721/set-initial-owner.png) - -::: - - - -### Mint Your Custom NFTs - -So far, you have [created](#create-the-smart-contract) and deployed the contract. But, you probably want to mint some NFTs. -To do, you should: - -1. Open the contract (listed under `Deployed Contracts`). -2. Insert your target IOTA EVM in beside the `safeMint` button and then click the button. - - ![Safe mint](/img/iota-evm/evm/how-tos/ERC721/safe-mint.png) - -3. Confirm the transaction on Metamask. - - ![Confirm in metamask](/img/iota-evm/evm/how-tos/ERC721/confirm-in-metamask.png) - -If you visit your address in the visit the [IOTA EVM Explorer](https://explorer.evm.iota.org), -[ShimmerEVM Explorer](https://explorer.evm.testnet.shimmer.network/) or [EVM Testnet Explorer](https://explorer.evm.testnet.shimmer.network/) -you should see your NFTs listed under `Tokens`. diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/allow.md b/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/allow.md deleted file mode 100644 index a2ab2bb4797..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/allow.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -description: How to allow native assets and base token -image: /img/logo/WASP_logo_dark.png -tags: - - allowance - - evm - - magic - - solidity - - how-to ---- - -# Allow - -The allowance concept is well known from the EVM contracts like ERC20. -In ISC, we have a similar concept for our native assets. You might want to use this, for example, to [send native assets to L1](../send-assets-to-l1.mdx) (which includes sending tokens to other L1 chain accounts). - -## Example Code - -### 1. Create the `allow` Function - -Create a function which allows an address or contract to access a specific ID of your account: - -```solidity -function allow(address _address, bytes32 _allowanceNFTID) public { -``` - -### 2. Create the `ISCAssets` object - -Create an `ISCAssets` object to pass as allowance: - -```solidity -NFTID[] memory nftIDs = new NFTID[](1); -nftIDs[0] = NFTID.wrap(_allowanceNFTID); -ISCAssets memory assets; -assets.nfts = nftIDs; -``` - -### 3. Use the Assets as Allowance - -With that asset, you can call `allow` to allow address to take our NFT: - -```solidity -ISC.sandbox.allow(_address, assets); -``` - -## Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract Allowance { - function allow(address _address, bytes32 _allowanceNFTID) public { - NFTID[] memory nftIDs = new NFTID[](1); - nftIDs[0] = NFTID.wrap(_allowanceNFTID); - ISCAssets memory assets; - assets.nfts = nftIDs; - ISC.sandbox.allow(_address, assets); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/get-allowance.md b/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/get-allowance.md deleted file mode 100644 index 40016cdcfb0..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/get-allowance.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: How to get the allowance of native assets and base token -image: /img/logo/WASP_logo_dark.png -tags: - - allowance - - evm - - magic - - solidity - - how-to ---- - -# Get Allowance - -## Example Code - -There are multiple ways to check for an allowance. - -### `getAllowanceFrom()` - -`getAllowanceFrom()` fetches the funds currently allowed by the given address to the caller: - -```soliditiy -function getAllowanceFrom(address _address) public { - ISCAssets assets = ISC.sandbox.getAllowanceFrom(_address); - emit AllowanceFrom(assets) -} -``` - -### `getAllowanceTo()` - -`getAllowanceTo()` fetches the funds currently allowed by the caller to the given address: - -```soliditiy -function getAllowanceTo(address _target) public { - ISCAssets assets = ISC.sandbox.getAllowanceTo(_target); - emit AllowanceTo(assets) -} -``` - -### `getAllowance()` - -`getAllowance()` gets the funds currently allowed between the given addresses: - -```soliditiy -function getAllowance(address _from, address _to) public { - ISCAssets assets = ISC.sandbox.getAllowance(_from, _to); - emit Allowance(assets) -} -``` - -## Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract allowance { - event AllowanceFrom(ISCAssets assets) - event AllowanceTo(ISCAssets assets) - event Allowance(ISCAssets assets) - - function getAllowanceFrom(address _address) public { - ISCAssets assets = ISC.sandbox.getAllowanceFrom(_address); - emit AllowanceFrom(assets) - } - - function getAllowanceTo(address _target) public { - ISCAssets assets = ISC.sandbox.getAllowanceTo(_target); - emit AllowanceTo(assets) - } - - function getAllowance(address _from, address _to) public { - ISCAssets assets = ISC.sandbox.getAllowance(_from, _to); - emit Allowance(assets) - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/take-allowance.md b/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/take-allowance.md deleted file mode 100644 index 190c7455fa1..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/allowance/take-allowance.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -description: How to take the allowance of native assets and base token -image: /img/logo/WASP_logo_dark.png -tags: - - allowance - - evm - - magic - - solidity - - how-to ---- - -# Take allowed Funds - -After having [allowed](./allow.md) native assets, you can take the ones you need. - -## Example Code - -The following example will take the NFT which was allowed in the [allow how-to guide](./allow.md). - -### Create the `ISCAssets` - -First, you need to recreate the `ISCAssets` with the NFTID. - -```solidity -NFTID[] memory nftIDs = new NFTID[](1); -nftIDs[0] = NFTID.wrap(_allowanceNFTID); -ISCAssets memory assets; -assets.nfts = nftIDs; -``` - -### Call `takeAllowedFunds()` - -After that, you can call `takeAllowedFunds()` to take the allowance of the specified address/contract - -```solidity -ISC.sandbox.takeAllowedFunds(_address, NFTID); -``` - -## Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract allowance { - function takeAllowedFunds(address _address, bytes32 NFTID) { - NFTID[] memory nftIDs = new NFTID[](1); - nftIDs[0] = NFTID.wrap(_allowanceNFTID); - ISCAssets memory assets; - assets.nfts = nftIDs; - ISC.sandbox.takeAllowedFunds(_address, NFTID); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/get-balance.md b/docs/content/developer/iota-evm/how-tos/core-contracts/basics/get-balance.md deleted file mode 100644 index 67c272bd5ce..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/get-balance.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -description: How to get the balance of L1 assets on L2 -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - balance - - how-to ---- - -# Get Balance - -Once you have your L1 assets on L2, you might want to check their balance. This guide explains how to do so by calling the three functions `getL2BalanceBaseTokens`, `getL2BalanceNativeTokens`and `getL2NFTAmount` for the corresponding token types. - -## Example Code - -1. Get the [AgentID](../../../explanations/how-accounts-work.md) from the sender by calling `ISC.sandbox.getSenderAccount()`. - -```solidity -ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); -``` - -2. To get the base token balance, you can call `getL2BalanceBaseTokens` using the `agentID`. - -```solidity -uint64 baseBalance = ISC.accounts.getL2BalanceBaseTokens(agentID); -``` - -3. To get the native token balance of a specific `NativeTokenID`, use `ISC.accounts.getL2BalanceNativeTokens` with the `id` and `agentID`. - -```solidity -NativeTokenID memory id = NativeTokenID({ data: nativeTokenID}); -uint256 nativeTokens = ISC.accounts.getL2BalanceNativeTokens(id, agentID); -``` - -4. To get the number of NFTs, use `ISC.accounts.getL2NFTAmount` with the `agentID`. - -```solidity -uint256 nfts = ISC.accounts.getL2NFTAmount(agentID); -``` - -### Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract GetBalance { - event GotAgentID(bytes agentID); - event GotBaseBalance(uint64 baseBalance); - event GotNativeTokenBalance(uint256 nativeTokenBalance); - event GotNFTIDs(uint256 nftBalance); - - function getBalance(bytes memory nativeTokenID) public { - ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); - emit GotAgentID(agentID.data); - - uint64 baseBalance = ISC.accounts.getL2BalanceBaseTokens(agentID); - emit GotBaseBalance(baseBalance); - - NativeTokenID memory id = NativeTokenID({ data: nativeTokenID}); - uint256 nativeTokens = ISC.accounts.getL2BalanceNativeTokens(id, agentID); - emit GotNativeTokenBalance(nativeTokens); - - uint256 nfts = ISC.accounts.getL2NFTAmount(agentID); - emit GotNativeTokenBalance(nfts); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/send-assets-to-l1.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/basics/send-assets-to-l1.mdx deleted file mode 100644 index e9daf665b4d..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/basics/send-assets-to-l1.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -description: The ISC Magic Contract allows EVM contracts to access ISC functionality. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - magic - - solidity ---- - -import AboutAccounts from '../../../../../_snippets/iota-evm/about-accounts.md'; - -# Send Assets and Tokens to L1 - - - -:::info - -IOTA EVM and ShimmerEVM have 18 decimal places, while IOTA and Shimmer have 6. This means that any decimals beyond the 6th will be ignored by IOTA and Shimmer, even though you can see them on IOTA EVM and ShimmerEVM. Please keep this in mind while sending your tokens to L1. - -::: - -This guide will show you how to use the ISC sandbox interface to send assets from L2 to L1. This includes base tokens, native tokens, and NFTs. Before you can send these assets, you need to know how you get them on L2 and how you allow a contract to use them. - -Note that assets on L1 require a storage deposit; therefore, the number of base tokens sent to L1 should cover at least the storage deposit required to hold the assets on L1. - -## Explanation - -First, you will find out what assets this contract is allowed to take from the caller by calling the `ISC.sandbox.getAllowanceFrom()` function. - -```solidity -ISCAssets memory allowance = ISC.sandbox.getAllowanceFrom(msg.sender); -``` - -Then you take the allowance, which will transfer the assets from the caller to the contract. - -```solidity -ISC.sandbox.takeAllowedFunds(msg.sender, allowance); -``` - -Finally, you can send the assets to the specified L1 address. This will create an output to hold said assets. You can use additional options to add the timelock and expiration unlock conditions to the output. - -```solidity -ISCSendMetadata memory metadata; -ISCSendOptions memory options; -ISC.sandbox.send(to, allowance, false, metadata, options); -``` - -## Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract L1Assets { - function withdraw(L1Address memory to) public { - ISCAssets memory allowance = ISC.sandbox.getAllowanceFrom(msg.sender); - ISC.sandbox.takeAllowedFunds(msg.sender, allowance); - - ISCSendMetadata memory metadata; - ISCSendOptions memory options; - ISC.sandbox.send(to, allowance, false, metadata, options); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/call-view.md b/docs/content/developer/iota-evm/how-tos/core-contracts/call-view.md deleted file mode 100644 index 5ef9d261cfa..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/call-view.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -description: With call and callView you can interact with any core contract -image: /img/logo/WASP_logo_dark.png -tags: - - magic - - core-contract - - evm - - solidity ---- - -# Interact with any Core contract - -## About `call` and `callView` - -The magic contract provides you with a solidity interface to the core contracts. Some functions like [`getL2BalanceBaseTokens`](../../../../references/iota-evm/magic-contract/ISCAccounts.md#getl2balancebasetokens) are wrapped in the magic contract directly, others you need to call yourself. You can do that with the [`call`](../../../../references/iota-evm/magic-contract/ISCSandbox.md#call) and [`callView`](../../../../references/iota-evm/magic-contract/ISCSandbox.md#callview) functions. - -:::info WASM - -You can also use `call` and `callView` to interact with WASM contracts. - -::: - -## Example Code - -1. Get the [`AgentID`](../../explanations/how-accounts-work.md) from the sender by calling ISC.sandbox.getSenderAccount(). - -```solidity -ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); -``` - -2. Initialize the parameters for the call by creating a new [`ISCDict`](../../../../references/iota-evm/magic-contract/ISCTypes.md#iscdict). As you can see in the docs, [`getl2balancenativetokens`](../../../../references/iota-evm/magic-contract/ISCAccounts.md#getl2balancenativetokens) takes two parameters.: the Agent ID and the native token ID. So, you have to create a dictionary with two key-value pairs. The key of the first pair (Agent ID) has to be `a` and the key for the second pair (native token ID) `N`. - -```solidity -ISCDict memory params = ISCDict(new ISCDictItem[](2)); -params.items[0] = ISCDictItem("a", agentID.data); -params.items[1] = ISCDictItem("N", nativeTokenID); -``` - -3. Now, you can use [`callView`](../../../../references/iota-evm/magic-contract/ISCSandbox.md#callview) to call our contract. The first parameter is the core contract `hname`, which we can get with the helper utility [`hn`](../../../../references/iota-evm/magic-contract/ISCUtil.md#hn), and the second parameter is the function we want to call. The last parameter is the dictionary with all function parameters. - -```solidity -ISCDict memory result = ISC.sandbox.callView( - ISC.util.hn("accounts"), - ISC.util.hn("balanceNativeToken"), - params -); -``` - -4. Next, you can either return or emit the result. - -```solidity -emit NativeTokenBalance(bytesToUint(result.items[0].value)); -``` - -:::info Return Dictionary - -Keep in mind that the call and callView functions will always return a dictionary. The values of this dictionary are always of type byte, so you need to take care of converting it yourself. - -::: - -### Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract NativeTokenBalance { - event NativeTokenBalance(uint balance); - - function getNativeTokenBalance(bytes memory nativeTokenID) public { - ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); - - ISCDict memory params = ISCDict(new ISCDictItem[](2)); - params.items[0] = ISCDictItem("a", agentID.data); - params.items[1] = ISCDictItem("N", nativeTokenID); - - ISCDict memory result = ISC.sandbox.callView( - ISC.util.hn("accounts"), - ISC.util.hn("balanceNativeToken"), - params - ); - - emit NativeTokenBalance(bytesToUint(result.items[0].value)); - } - - function bytesToUint(bytes memory b) internal pure virtual returns (uint256) { - require(b.length <= 32, "Bytes length exceeds 32."); - return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/get-randomness-on-l2.md b/docs/content/developer/iota-evm/how-tos/core-contracts/get-randomness-on-l2.md deleted file mode 100644 index 73d72c5387b..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/get-randomness-on-l2.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -description: You can use the ISC Magic Contract in EVM contracts to access ISC functionality, such as randomness. -image: /img/logo/WASP_logo_dark.png -tags: - - magic - - randomness - - evm - - solidity ---- - -# Get Randomness on L2 - -ISC introduces a feature not found on many other smart contract protocols: randomness. - -In the ISC protocol, for each state update, each validator proposes a batch of smart contract requests that they would like to process next. They commit to their proposals with a signature, after which a common subset of requests is found, and a combined signature is produced. - -This combined signature is unpredictable, and not only serves as protection against MEV by pseudo-randomly ordering requests, but also provides a source of verifiable entropy for randomness on L2. - -This guide will show you how you can use this entropy to generate random values in your contracts. - -:::info A note about entropy - -While entropy is random for each smart contract request, entropy is constant within a request. This means multiple calls to get entropy within the same request will return the same value. - -::: - -## Explanation - -When you want to generate multiple random values within a single request, you need to take into account that entropy is constant within a request. In this contract we use an increasing nonce in addition to the entropy, to make sure we are generating unique values: - -```solidity -uint256 private _nonce; - -function getNonce() internal returns (bytes32) { - return bytes32(_nonce++); -} -``` - -### Generating Integers - -To then generate a random integer, you can take the entropy and a unique nonce and hash them together: - -```solidity -bytes32 entropy = ISC.sandbox.getEntropy(); -bytes32 nonce = getNonce(); -bytes32 digest = keccak256(bytes.concat(entropy, nonce)); -``` - -And then cast the digest to an integer: - -```solidity -uint256 value = uint256(digest); -``` - -### Generating bytes - -Similarly to generating a random integer, you can generate any sequence of random bytes by taking the entropy and a unique nonce and hash them together: - -```solidity -bytes32 entropy = ISC.sandbox.getEntropy(); -bytes32 nonce = getNonce(); -bytes32 digest = keccak256(bytes.concat(entropy, nonce)); -``` - -And then repeatedly hash the digest and copy it in a sequence of bytes until you reach the required length. - -```solidity -bytes memory value = new bytes(length); -for (uint i = 0; i < length; i += 32) { - digest = keccak256(bytes.concat(digest)); - for (uint j = 0; j < 32 && i + j < length; j++) { - value[i + j] = digest[j]; - } -} -``` - -## Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract Random { - event Int(uint256); - event Bytes(bytes); - - uint256 private _nonce; - - function getNonce() internal returns (bytes32) { - return bytes32(_nonce++); - } - - function getInt() public returns (uint256) { - bytes32 entropy = ISC.sandbox.getEntropy(); - bytes32 nonce = getNonce(); - bytes32 digest = keccak256(bytes.concat(entropy, nonce)); - - uint256 value = uint256(digest); - - emit Int(value); - return value; - } - - function getBytes(uint length) public returns (bytes memory) { - bytes32 entropy = ISC.sandbox.getEntropy(); - bytes32 nonce = getNonce(); - bytes32 digest = keccak256(bytes.concat(entropy, nonce)); - - bytes memory value = new bytes(length); - for (uint i = 0; i < length; i += 32) { - digest = keccak256(bytes.concat(digest)); - for (uint j = 0; j < 32 && i + j < length; j++) { - value[i + j] = digest[j]; - } - } - - emit Bytes(value); - return value; - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/introduction.md b/docs/content/developer/iota-evm/how-tos/core-contracts/introduction.md deleted file mode 100644 index bbbdc1d6fbb..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/introduction.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -description: The ISC Core Contracts allows VMs to access ISC functionality. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - magic - - solidity - - rpc ---- - -# The Core Contracts - -The [core contracts](../../explanations/core-contracts.md) are contracts deployed on every chain and are vital to interact with L1 and the chain itself. They can be called in Solidity through the [ISC Magic Contract](../../../../references/iota-evm/magic-contract/introduction.md). - -## The ISC Magic Contract - -The Magic contract is an EVM contract deployed by default on every ISC chain, in the EVM genesis block, at -address `0x1074000000000000000000000000000000000000`. -The implementation of the Magic contract is baked-in in -the [`evm`](../../../../references/iota-evm/core-contracts/evm.md) [core contract](../../../../references/iota-evm/core-contracts/overview.md); -i.e. it is not a pure-Solidity contract. - -The Magic contract has several methods, which are categorized into specialized -interfaces: `ISCSandbox`, `ISCAccounts`, `ISCUtil` and so on. -You can access these interfaces from any Solidity contract by importing -the [ISC library](https://www.npmjs.com/package/@iota/iscmagic): - -```bash npm2yarn -npm install @iota/iscmagic -``` - -You can import it into your contracts like this: - -```solidity -import "@iota/iscmagic/ISC.sol"; -``` - -The Magic contract also provides proxy ERC20 contracts to manipulate ISC base -tokens and native tokens on L2. - -:::info Reference Docs - -If you need further info about magic contracts interfaces you can check out the [magic contract docs](../../../../references/iota-evm/magic-contract/introduction.md). - -::: - -## Call a Function - -:::info Ease of use - -To make it easier for developers to use the core contracts, you should, in most cases, run the functions from the magic contract directly. For example, to get the native token balance, you could [call the `balanceNativeToken()`](./call-view.md) directly with `callView`, or use [`getl2balancenativetokens`](./basics/get-balance.md) of the magic contract, or (the suggested way) register your native token as [`ERC20`](../../../../references/iota-evm/magic-contract/ERC20NativeTokens.md) and call the standard [`balanceof`](../../../../references/iota-evm/magic-contract/ERC20NativeTokens.md#balanceof) function. What you use also depends on what you optimize for. For example, to save gas, it could be interesting for you to call core contracts from your favorite web3 library directly and compute other things off-chain. - -::: - -In the example below, `ISC.sandbox.getEntropy()` calls the -[`getEntropy`](https://github.com/iotaledger/wasp/blob/develop/packages/vm/core/evm/iscmagic/ISCSandbox.sol#L20) -method of the `ISCSandbox` interface, which, in turn, -calls [ISC Sandbox's](../../explanations/sandbox.md) `GetEntropy`. - -```solidity -pragma solidity >=0.8.5; - -import "@iota/iscmagic/ISC.sol"; - -contract MyEVMContract { - event EntropyEvent(bytes32 entropy); - - // this will emit a "random" value taken from the ISC entropy value - function emitEntropy() public { - bytes32 e = ISC.sandbox.getEntropy(); - emit EntropyEvent(e); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/introduction.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/nft/introduction.mdx deleted file mode 100644 index 0a257f8de35..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/introduction.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: How to handle native NFTs on L2 and use them as ERC721 -image: /img/logo/WASP_logo_dark.png -tags: - - nft - - evm - - how-to ---- - -import DocCardList from '@theme/DocCardList'; - -# Native NFT and ERC721 - -The IOTA L1 can create NFTs, also called native NFTs. To use these NFTs on L2, you have -an ERC20 contract called `ERC721NFTs`, which contains all L1 NFTs owned by the chain. The following guides will show you how to [mint your own L1 NFT from L2](./mint-nft.mdx and [use](./use-as-ERC721.mdx it with the `ERC721NFTs` contract. - - \ No newline at end of file diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/mint-nft.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/nft/mint-nft.mdx deleted file mode 100644 index 50fc26f91b3..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/mint-nft.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -description: How to mint L1 NFT -image: /img/logo/WASP_logo_dark.png -tags: - - nft - - evm - - how-to ---- - -import ExampleCodeIntro from '../../../../../_snippets/iota-evm/how-tos/token/example_code_intro.mdx'; - -# Mint an NFT - -## About NFTs - -The Stardust update allows you to create your own NFTs. You can also use [IRC27](https://github.com/iotaledger/tips/blob/main/tips/TIP-0027/tip-0027.md) for NFTs. This guide will show you how to create an IRC27 L1 NFT using a L2 smart contract. - -## Example Code - - - -2. Get the senders AgentID: - -```solidity -ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); -``` - -3. Create an `IRC27Metadata` struct with all the needed data: - -```solidity -IRC27NFTMetadata memory metadata = IRC27NFTMetadata({ - standard: "IRC27", - version: "v1.0", - mimeType: _mimeType, - uri: _uri, - name: _name -}); -``` - -4. Create all the data for the core contract call. To do so, you should create a new `ISCDict` with 2 parameters like specified in the reference docs for [`mintNFT`](../../../../../references/iota-evm/core-contracts/accounts.md#mintnfti-immutabledata-a-agentid-c-collectionid-w-withdrawonmint) - -- `I` is the immutable metadata we fill with the IRC27 metadata and -- `a` is the AgendID of the owner of the NFT - -```solidity -ISCDict memory params = ISCDict(new ISCDictItem[](2)); -params.items[0] = ISCDictItem("I", bytes(IRC27NFTMetadataToString(metadata))); -params.items[1] = ISCDictItem("a", agentID.data); -``` - -:::info IRC27NFTMetadataToString - -The full example below calls the `IRC27NFTMetadataToString` function, which simply converts the IRC27Metadata struct into a string. - -::: - -5. Call the magic contract `call` function with all the parameters. You should specify the core contract you want to call, which in this case is the [`account`](../../../../../references/iota-evm/core-contracts/accounts.md) contract, and the function for [minting an NFT](../../../../../references/iota-evm/core-contracts/accounts.md#mintnfti-immutabledata-a-agentid-c-collectionid-w-withdrawonmint) - -```solidity -ISCDict memory ret = ISC.sandbox.call( - ISC.util.hn("accounts"), - ISC.util.hn("mintNFT"), - params, - allowance -); -``` - -6. The call return value will contain a `mintID` which we can use in, for example, another contract function to get the actual L1 NFT ID once it is created using the [`accounts.NFTIDbyMintID`](../../../../../references/iota-evm/core-contracts/accounts.md#nftidbymintidd-mintid) function - -```solidity -function getNFTIDFromMintID(bytes memory mintID) public view returns (bytes memory) { - ISCDict memory params = ISCDict(new ISCDictItem[](1)); - params.items[0] = ISCDictItem("D", mintID); - - ISCDict memory ret = ISC.sandbox.callView( - ISC.util.hn("accounts"), - ISC.util.hn("NFTIDbyMintID"), - params - ); - return ret.items[0].value; -} -``` - -### Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract NFTContract { - event MintedNFT(bytes mintID); - - function mintNFT(string memory _name, string memory _mimeType, string memory _uri, uint64 _storageDeposit) public payable { - require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit"); - ISCAssets memory allowance; - allowance.baseTokens = _storageDeposit; - - ISCAgentID memory agentID = ISC.sandbox.getSenderAccount(); - - IRC27NFTMetadata memory metadata = IRC27NFTMetadata({ - standard: "IRC27", - version: "v1.0", - mimeType: _mimeType, - uri: _uri, - name: _name - }); - - ISCDict memory params = ISCDict(new ISCDictItem[](2)); - params.items[0] = ISCDictItem("I", bytes(IRC27NFTMetadataToString(metadata))); - params.items[1] = ISCDictItem("a", agentID.data); - - ISCDict memory ret = ISC.sandbox.call( - ISC.util.hn("accounts"), - ISC.util.hn("mintNFT"), - params, - allowance - ); - emit MintedNFT(ret.items[0].value); - } - - function getNFTIDFromMintID(bytes memory mintID) public view returns (bytes memory) { - ISCDict memory params = ISCDict(new ISCDictItem[](1)); - params.items[0] = ISCDictItem("D", mintID); - - ISCDict memory ret = ISC.sandbox.callView( - ISC.util.hn("accounts"), - ISC.util.hn("NFTIDbyMintID"), - params - ); - return ret.items[0].value; - } - - function IRC27NFTMetadataToString(IRC27NFTMetadata memory metadata) - public - pure - returns (string memory) - { - return string.concat( - '{"standard": "', - metadata.standard, - '", "version": "', - metadata.version, - '", "type": "', - metadata.mimeType, - '", "uri": "', - metadata.uri, - '", "name": "', - metadata.name, - '"}'); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/use-as-erc721.md b/docs/content/developer/iota-evm/how-tos/core-contracts/nft/use-as-erc721.md deleted file mode 100644 index 010174f7724..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/nft/use-as-erc721.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: How to use a native NFT like an ERC721 NFT -image: /img/logo/WASP_logo_dark.png -tags: - - nft - - evm - - how-to - - erc721 ---- - -# Use as ERC721 - -## About the `ERC721NFTs` contract - -The `ERC721NFTs` contract is a hardcoded contract at address `0x1074030000000000000000000000000000000000`. Every L1 NFT owned by the chain can be accessed through it. In this example, we will show you how to use it by using `transferFrom`. - -## Example Code - -1. ERC721 uses the `tokenID` for almost all interactions with it. So first, you should convert the `NFTID` to a `tokenID`: - -```solidity -uint256 tokenID = uint256(NFTID.unwrap(nftID)); -``` - -:::info Token ID to NFT ID - -You can use the ISCTypes.asNFTID() function to convert a Token ID to an NFT ID, by either using it on a token ID `tokenID.asNFTID();` or passing it to function `ISCTypes.asNFTID(tokenID)`. - -::: - -2. Transfer the token with ID `tokenID`, by using the `ERC20NFTs` contract, which is exposed in the library as `ISC.nfts`, and calling the standard `transferFrom` function: - -```solidity -ISC.nfts.transferFrom(msg.sender, _destination, tokenID); -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-foundry.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-foundry.mdx deleted file mode 100644 index 087d7816bdf..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-foundry.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -description: How to create a L1 foundry -image: /img/logo/WASP_logo_dark.png -tags: -- evm -- how-to ---- - - -import ExampleCodeIntro from '../../../../../_snippets/iota-evm/how-tos/token/example_code_intro.mdx'; -import ObsoleteTokenCreation from '../../../../../_snippets/iota-evm/how-tos/token/obsolete_token_creation.md'; - -# Create a Foundry - - - -## About Foundries - -The Stardust update allows you to create your own native tokens. Native tokens are minted by a [Foundry](https://github.com/iotaledger/tips/blob/main/tips/TIP-0018/tip-0018.md#foundry-output). -The Foundry allows you to specify your native token's maximum supply **once** and change the circulating supply. -This guide will show you how to create an L1 foundry using a L2 smart contract. - -## Example Code - - - -### 2. Define the Token Scheme - -Define the `NativeTokenScheme` by specifying its `mintedTokens`, `meltedTokens` and `maximumSupply`: - -```solidity -NativeTokenScheme memory nativeTokenScheme = NativeTokenScheme({ - mintedTokens: _mintedTokens, - meltedTokens: _meltedTokens, - maximumSupply: _maximumSupply -}); -``` - -### 3. Create the Foundry - -Create the foundry by calling the `ISC.accounts.foundryCreateNew(nativeTokenScheme, allowance)` function: - -```solidity -uint32 foundrySN = ISC.accounts.foundryCreateNew(nativeTokenScheme, allowance); -``` - -### Full Example Code - -```solidity -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract CreateFoundry { - event CreatedFoundry(uint32 foundrySN); - - function createFoundry(uint _mintedTokens, uint _meltedTokens, uint _maximumSupply, uint64 _storageDeposit) public payable { - require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit"); - ISCAssets memory allowance; - allowance.baseTokens = _storageDeposit; - NativeTokenScheme memory nativeTokenScheme = NativeTokenScheme({ - mintedTokens: _mintedTokens, - meltedTokens: _meltedTokens, - maximumSupply: _maximumSupply - }); - uint32 foundrySN = ISC.accounts.foundryCreateNew(nativeTokenScheme, allowance); - emit CreatedFoundry(foundrySN); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-native-token.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-native-token.mdx deleted file mode 100644 index 9ea6be3c50b..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/create-native-token.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -description: How to Create a Native Token Foundry. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - how-to - - native-token ---- - -import ExampleCodeIntro from '../../../../../_snippets/iota-evm/how-tos/token/example_code_intro.mdx'; - -# Create a Native Token - -This guide will show you how you can efficiently mint new tokens and register them for use as ERC20 tokens with the [`createNativeTokenFoundry`](../../../../../references/iota-evm/magic-contract/ISCAccounts.md#createnativetokenfoundry) function in one seamless operation. It will create a foundry on L1 and register it as an ERC20 on L2. This method ensures that only the foundry owner can mint tokens, maintaining security and control over the token creation process. - -## About Foundries - -The Stardust update allows you to create your own native tokens. Native tokens are minted by a [Foundry](https://github.com/iotaledger/tips/blob/main/tips/TIP-0018/tip-0018.md#foundry-output). -The Foundry lets you specify your native token's maximum supply **once** and change the circulating supply. - -## Example Code - - - -### 2. Define the Token Scheme - -Define the [`NativeTokenScheme`](../../../../../references/iota-evm/magic-contract/ISCTypes.md#nativetokenscheme) by specifying the `maximumSupply`. - -```solidity -NativeTokenScheme memory nativeTokenScheme = NativeTokenScheme({ - mintedTokens: 0, - meltedTokens: 0, - maximumSupply: _maximumSupply -}); -``` - -### 3. Mint and Register Native Token - -Minting native tokens and registering them as ERC20 tokens using [`createNativeTokenFoundry`](../../../../../references/iota-evm/magic-contract/ISCAccounts.md#createnativetokenfoundry) method - -```solidity -uint32 foundrySN = ISC.accounts.createNativeTokenFoundry( - _tokenName, - _tokenSymbol, - _tokenDecimals, - nativeTokenScheme, - allowance -); -``` - -## Full Example Code - -```solidity -pragma solidity ^0.8.0; - -import "@iota/iscmagic/ISC.sol"; - -contract MyToken { - event MintedToken(uint32 foundrySN); - - constructor( - string memory _tokenName, - string memory _tokenSymbol, - uint8 _tokenDecimals, - uint256 _maximumSupply, - uint64 _storageDeposit - ) payable { - require(msg.value == _storageDeposit * (10**12), "Please send exact funds to pay for storage deposit"); - ISCAssets memory allowance; - allowance.baseTokens = _storageDeposit; - - NativeTokenScheme memory nativeTokenScheme = NativeTokenScheme({ - mintedTokens: 0, - meltedTokens: 0, - maximumSupply: _maximumSupply - }); - - uint32 foundrySN = ISC.accounts.createNativeTokenFoundry( - _tokenName, - _tokenSymbol, - _tokenDecimals, - nativeTokenScheme, - allowance - ); - emit MintedToken(foundrySN); - } -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/erc20-native-token.md b/docs/content/developer/iota-evm/how-tos/core-contracts/token/erc20-native-token.md deleted file mode 100644 index 54c7baac6ef..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/erc20-native-token.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -description: How to use the custom functionality of ERC20NativeToken -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - erc20 - - how-to ---- - -# Custom ERC20 Functions - -Once you [registered your native token as ERC20](./erc20-native-token.md) you can use it like any other ERC20 token, with functions like -`transfer`, `balanceOf`, etc. But, as the ERC20 token maps the native token on L2, there are some additional ISC features -you can take advantage of. - -## Example Code - -### Get Your `nativeTokenID` - -You can use the `erc20NativeTokensAddress` function and the Foundry serial number to get the contract address: - -```solidity -ERC20NativeTokens token = ERC20NativeTokens( - ISC.sandbox.erc20NativeTokensAddress(_foundrySN) -); -``` - -- `nativeTokenID` will give you the native token ID of the ERC20 token: - -```solidity -NativeTokenID memory id =token.nativeTokenID(); -``` - -### Full Example Code - -```solidity -function nativeTokenID(uint32 _foundrySN) public view returns (bytes memory) { - ERC20NativeTokens token = ERC20NativeTokens( - ISC.sandbox.erc20NativeTokensAddress(_foundrySN) - ); - NativeTokenID memory id =token.nativeTokenID(); - return id.data; -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/introduction.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/token/introduction.mdx deleted file mode 100644 index fd591602d3f..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/introduction.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: How to handle native tokens on L2 and use them as ERC20 -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - erc20 - - native-token - - how-to ---- - -import DocCardList from '@theme/DocCardList'; - -# Native Token and ERC20NativeToken - -The IOTA L1 has functionality to create L1 tokens, also called native tokens. To use these native tokens on L2, you have -a ERC20 contract called `ERC20NativeToken`. The following guides will show you how to [create a foundry](create-foundry.mdx -that can [mint your own L1 token from L2](mint-token.mdx, and [register](register-token.mdx it as `ERC20NativeToken`, -so it can be used like any other ERC20 token with some additional features. - - \ No newline at end of file diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/mint-token.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/token/mint-token.mdx deleted file mode 100644 index 240f0252e4c..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/mint-token.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: How to mint native token on an L1 foundry. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - how-to - - native-token ---- - -import ExampleCodeIntro from '../../../../../_snippets/iota-evm/how-tos/token/example_code_intro.mdx'; - -# Mint Native Tokens - -To mint tokens from a [foundry](https://github.com/iotaledger/tips/blob/main/tips/TIP-0018/tip-0018.md#foundry-output), you first need to be aware that only the foundry owner can mint token, so you should execute the `ISC.accounts.mintNativeTokens` function in the same contract where you also [created the native token](./create-native-token.mdx. - -## Example Code - - - -### 2. Mint the Native Token - -Mint the native token specifying the foundry serial number, the amount to mint and the allowance. - -```solidity -ISC.accounts.mintNativeTokens(_foundrySN, _amount, allowance); -``` - -## Full Example Code - -```solidity -event MintedNativeTokens(uint32 foundrySN, uint amount); - -function mintNativeTokens(uint32 _foundrySN, uint _amount, uint64 _storageDeposit) public payable { - require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit"); - ISCAssets memory allowance; - allowance.baseTokens = _storageDeposit; - ISC.accounts.mintNativeTokens(_foundrySN, _amount, allowance); - emit MintedNativeTokens(_foundrySN, _amount); -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/core-contracts/token/register-token.mdx b/docs/content/developer/iota-evm/how-tos/core-contracts/token/register-token.mdx deleted file mode 100644 index f99280e79ba..00000000000 --- a/docs/content/developer/iota-evm/how-tos/core-contracts/token/register-token.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: How to register a native token as ERC20 -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - native-token - - how-to ---- - -import ExampleCodeIntro from '../../../../../_snippets/iota-evm/how-tos/token/example_code_intro.mdx'; -import ObsoleteTokenCreation from '../../../../../_snippets/iota-evm/how-tos/token/obsolete_token_creation.md'; - -# Register Tokens - - - -To properly use your native tokens, you should register them as ERC20 using the `registerERC20NativeToken` function from the ISC magic contract. - -## Example Code - - - -### 2. Register the Native Tokens - -Register the native tokens specifying: - -- the foundry serial number -- a name -- a symbol -- it's decimals -- the allowance. - -```solidity -ISC.sandbox.registerERC20NativeToken(_foundrySN, _name, _symbol, _decimals, allowance); -``` - -### 3. Get the Contract's Address - -Get the ERC20 contract address with `erc20NativeTokensAddress`: - -```solidity -address erc20address = ISC.sandbox.erc20NativeTokensAddress(_foundrySN); -``` - -### Full Example Code - -```solidity -event ERC20Address(address erc20address); - -function registerERC20NativeToken(uint32 _foundrySN, string calldata _name, string calldata _symbol, uint8 _decimals, uint64 _storageDeposit) public payable { -require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit"); -ISCAssets memory allowance; -allowance.baseTokens = _storageDeposit; -ISC.sandbox.registerERC20NativeToken(_foundrySN, _name, _symbol, _decimals, allowance); -address erc20address = ISC.sandbox.erc20NativeTokensAddress(_foundrySN); -emit ERC20Address(erc20address); -} -``` diff --git a/docs/content/developer/iota-evm/how-tos/create-a-basic-contract.mdx b/docs/content/developer/iota-evm/how-tos/create-a-basic-contract.mdx deleted file mode 100644 index 8445a773116..00000000000 --- a/docs/content/developer/iota-evm/how-tos/create-a-basic-contract.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -description: Basic smart contract example. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm ---- - -import DeployAdmonition from '../../../_snippets/iota-evm/deploy_a_smart_contract.md'; - -# Basic Smart Contract Example - -[Solidity](https://docs.soliditylang.org/en/v0.8.16/) smart contracts on IOTA Smart Contracts are compatible with -Solidity smart contracts on any other network. Most smart contracts will work directly without any modification. To get -a rough indication of what a simple Solidity smart contract looks like, see the example below: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -contract Counter { - uint private count; - - constructor() { - count = 0; - } - - function increment() public { - count += 1; - } - - function decrement() public { - require(count > 0, "Count is already zero"); - count -= 1; - } - - function getCount() public view returns (uint) { - return count; - } -} - -``` - -This contract simply updates a `count` variable. It has -three [entry points](../explanations/smart-contract-anatomy.md#entry-points): - -- `increment` and `decrement`: Two full entry points that can alter - the [state](../explanations/smart-contract-anatomy.md#state), i.e. the `count variable`. -- `getCount`: A view only entry point, which simply renders the current `count` state. - -For more information, please visit the [official Solidity documentation](https://docs.soliditylang.org/). - - diff --git a/docs/content/developer/iota-evm/how-tos/deploy-a-smart-contract.mdx b/docs/content/developer/iota-evm/how-tos/deploy-a-smart-contract.mdx deleted file mode 100644 index e488b29e15c..00000000000 --- a/docs/content/developer/iota-evm/how-tos/deploy-a-smart-contract.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -description: Learn how to deploy smart contracts to IOTA EVM, Shimmer EVM and EVM - Testnet using popular tools like Remix and Hardhat. -tags: - - how-to - - evm - - getting-started -image: /img/logo/WASP_logo_dark.png ---- - -import { AddToMetaMaskButton } from '@site/src/components/AddToMetaMaskButton'; -import HardhatConfig from '../../../_snippets/iota-evm/hardhat_config.mdx'; -import MetamaskButtons from '../../../_snippets/iota-evm/metamask_buttons.mdx'; -import { Networks } from '@site/src/components/constant'; - -# Deploy a Smart Contract - -You can deploy your smart contracts to any of your EVM chains using popular tools like Remix -and Hardhat. - -:::tip Get a Basic Contract - -This guide will deploy the `Counter` contract you can find in -the [How to Create a Basic Solidity Contract Guide](create-a-basic-contract.mdx. - -::: - - - - -Deploying a Solidity smart contract using Remix is a straightforward process that doesn't require any installation or -setup of development environments on your machine. Remix is a powerful, browser-based IDE that allows you to write, -compile, and deploy smart contracts. - -### 1. Connect to Metamask - -Before you get started, make sure you have connected Metamask to your network of choice. - -:::tip Networks & Endpoints - -You can check the connection details in the Networks & Endpoints section. - -::: - - - -### 2. Access Remix IDE - -Open your web browser and navigate to [Remix IDE](https://remix.ethereum.org/). - -### 3. Create Your Smart Contract - -1. In the `File Explorer` tab on the left, click the `Create New File` icon. -2. Name your file `Counter.sol`. -3. Copy the Solidity code for the [basic counter smart contract](create-a-basic-contract.mdx and paste it into - the `Counter.sol` file you just created in Remix. - -### 4. Compile Your Smart Contract - -1. Navigate to the `Solidity Compiler` tab on the left sidebar. -2. Select the appropriate compiler version that matches the version specified in your contract (`^0.8.6` or similar). - You might need to enable "Auto compile" or click the "Compile" button manually. -3. If there are errors, Remix will display them, and you'll need to correct them before proceeding. - -### 5. Deploy Your Smart Contract - -1. Switch to the "Deploy & Run Transactions" tab on the left sidebar. -2. In the "Environment" dropdown, select and select `Injected Web3` from the `Environment` dropdown. - - ![Select Injected Provider from the Environment dropdown](/img/iota-evm/evm/remix-injected-provider-metamask.png) - -3. After selecting the environment, make sure the contract Counter is selected in the `Contract` dropdown. -4. Click the `Deploy` button. If you're using an Ethereum network, confirm the transaction in your Web3 wallet. - -### 6. Interact with Your Deployed Contract - -Once deployed, the contract instance will appear under the `Deployed Contracts` section. -Here, you can interact with your contract by calling its functions. For the Counter contract, you'll see buttons to call -its `increment`, `decrement`, and `getCount` functions directly from Remix. - - - - -The first thing you'll need to deploy a smart contract using [Hardhat](https://hardhat.org/) is to set up a Hardhat -project. Here's a step-by-step guide: - -### Requirements - -- [Node.js](https://nodejs.org/). -- [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/). - -### 1. Set Up Hardhat - -1. Open a new terminal window. -2. Create a new directory for your project, and navigate into it. For example: - ```bash - mkdir deploy-a-basic-contract && - cd deploy-a-basic-contract - ``` -3. Create a new node project by running: - ```bash - npm init -y - ``` -4. Install Hardhat by running: - ```bash - npm install --save-dev hardhat - ``` -5. Create a Hardhat Project by running the following command: - - ```bash - npx hardhat init - ``` - - Select `Create a JavaScript project` (or whatever applies to your project) when prompted and answer the setup questions (you can press enter to - accept defaults). - -### 2. Add Your Contract - -1. Inside the `contracts` folder, create a new file called `Counter.sol` and paste the content of - the [Counter Basic Contract](create-a-basic-contract.mdx. - -### 3. Create a Deployment Script - -1. Navigate to the `scripts` folder. -2. Create a new file called `deploy.js` with the following code: - - ```javascript - async function main() { - const Counter = await ethers.getContractFactory('Counter'); - const counter = await Counter.deploy(); - - console.log('Counter deployed to:', await counter.getAddress()); - } - - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - ``` - -### 4. Compile and Deploy Your Contract - -1. Add your preferred network parameters to the `hardhat.config.js`, for example: - - - -:::info Export the Metamask Private Key - -1. Click on the Metamask logo in the upper right corner. -2. Select the account you want to export. -3. On the account page, click the menu (three dots) in the upper right corner, then click the "Account Details" button. -4. Click on "Export Private Key". -5. Enter your wallet password to access your private key and click `Confirm` to continue. -6. Your private key will now be displayed. Click to copy it and save it in a safe place. - -You can find more information in the [official Metamask Documentation](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/). - -::: - -:::caution - -Currently, there is no validation service available for EVM/Solidity smart contracts on IOTA Smart Contracts, which is -often offered through block explorer APIs. - -::: - -2. Compile your contract by running the following command: - - ```bash - npx hardhat compile - ``` - -3. If you have no compilation errors, you can deploy your contract by running the following command: - - ```bash - npx hardhat run scripts/deploy.js --network evm-testnet - ``` - - **Expected output**: - - ```bash - Counter deployed to: 0x123456789ABCDEFGHIJK123456789ABCDEFGHIJK - ``` - - **\*** `0x123456789ABCDEFGHIJK123456789ABCDEFGHIJK` is the contract unlock address. - -4. You can verify your contract by visiting - the [EVM Testnet Explorer](https://explorer.evm.testnet.shimmer.network/), - and searching for the address from the previous step. If you access the `Contract` tab, you should be able to see - your code and interact with your contract. - - - diff --git a/docs/content/developer/iota-evm/how-tos/introduction.md b/docs/content/developer/iota-evm/how-tos/introduction.md deleted file mode 100644 index e1456e89112..00000000000 --- a/docs/content/developer/iota-evm/how-tos/introduction.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -description: Explore the how-to guides for IOTA Smart Contracts, offering step-by-step instructions on various topics and functionalities. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - explanation - - how-to ---- - -# About How-to Guides - -This section has several how-to guides that provide step-by-step instructions on various topics and functionalities. You -can use these guides to understand and implement specific tasks or features within your application. Whether you are a -beginner or an experienced developer, the how-to guides offer clear and concise explanations, making integrating IOTA smart contracts -functionality into your projects easier. - -To make the most of the how-to guides in this section, identify the specific topic or functionality you want to explore. -Whether you want to [get funds on L2](send-funds-from-L1-to-L2.mdx), [add ISC specific functionality](introduction.md), -or [send funds to L1](./core-contracts/basics/send-assets-to-l1.mdx), you can find dedicated guides that walk you through the process. - -With the how-to guides in the IOTA SDK, you can overcome implementation hurdles and gain a deeper understanding -of the IOTA ecosystem. These guides empower you with the knowledge and practical examples to seamlessly integrate IOTA -functionality into your projects. diff --git a/docs/content/developer/iota-evm/how-tos/send-ERC20-across-chains.md b/docs/content/developer/iota-evm/how-tos/send-ERC20-across-chains.md deleted file mode 100644 index e8d09b61b54..00000000000 --- a/docs/content/developer/iota-evm/how-tos/send-ERC20-across-chains.md +++ /dev/null @@ -1,296 +0,0 @@ -# Send ERC20 Tokens Across Chains - -## Introduction - -[LayerZero OFT V2](https://docs.layerzero.network/v2) enables cross-chain transfers of existing ERC20 tokens, such as wSMR and wIOTA (wrapped versions of the native gas tokens SMR and IOTA on ShimmerEVM and IOTA EVM respectively). For testing purposes, Sepolia is chosen as the source chain, while the BNB Testnet is chosen as the destination chain. - -:::info Community Libs - -You can clone the Utilities for LayerZero OFT V2 from [IOTA Community GitHub Repository](https://github.com/iota-community/layerzero-oft-v2-utils). - -::: - -### Why Would a User Need to Send ERC20 Tokens Across Chains? - -Sending ERC20 tokens across chains allows users to leverage different blockchain networks' strengths and unique features, optimize costs, and manage risks more effectively. This flexibility is crucial as the blockchain ecosystem continues to grow and diversify. - -#### Send Existing ERC20 Tokens Across Chains - -You need both the [OFT Adapter](https://docs.layerzero.network/v2/developers/evm/oft/adapter) and OFT contracts to enable existing ERC20 tokens for cross-chain sending, - -#### Create New Cross-chain Fungible Tokens - -If you are about to launch a new ERC20 token, you can use the [OFT standard](https://docs.layerzero.network/v2/developers/evm/oft/quickstart) to enable cross-chain sending without the OFT Adapter. - -## How To Use IOTA's Utilities for LayerZero OFT V2 - -The [Utilities for LayerZero OFT V2](https://github.com/iota-community/layerzero-oft-v2-utils) facilitate cross-chain sending of ERC20 tokens between a source chain (e.g., Sepolia or ShimmerEVM Testnet) and a destination chain (e.g., BNB Testnet or IOTA EVM Testnet). - -:::tip Further Information - -You can learn more about the available options in the [Layer Zero Documentation](https://docs.layerzero.network/v2/developers/evm/gas-settings/options#option-types.). - -::: - -### Send Tokens From One Source Chain to Another Destination Chain (and Vice Versa) - -To send existing ERC20 tokens, you will need both the OFT Adapter contract on the source chain and the OFT contract on the destination chain. You should then use the following procedure: - -#### 1. Approve the tokens - -The sender must approve their ERC20 tokens for the OFT Adapter contract. - -```typescript -const approveTx = await erc20TokenContract.approve(oftAdapterContractAddress, amountInWei); -``` - -#### 2. Estimate the fee - -The sender calls the function `quoteSend()` of the OFT Adapter contract to estimate the cross-chain fee to be paid in native tokens on the source chain. - -```typescript - const sendParam = [ - lzEndpointIdOnDestChain, - receiverAddressInBytes32, - amountInWei, - amountInWei, - options, // additional options - "0x", // composed message for the send() operation - "0x", // OFT command to be executed, unused in default OFT implementations - ]; - - // https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/oapp/contracts/oft/interfaces/IOFT.sol#L127C60-L127C73 - // false is set for _payInLzToken Flag indicating whether the caller is paying in the LZ token - const [nativeFee] = await myOFTAdapterContract.quoteSend(sendParam as any, false); -``` - -#### 3. Send the tokens - -The sender calls the function `send()` of the OFT Adapter contract to transfer tokens from the source chain to the destination chain. - -```typescript -const sendTx = await myOFTAdapterContract.send( - sendParam as any, - [nativeFee, 0] as any, // set 0 for lzTokenFee - sender.address, // refund address - { - value: nativeFee, - }, -); -const sendTxReceipt = await sendTx.wait(); -console.log("sendOFT - send tx on source chain:", sendTxReceipt?.hash); -``` - -#### 4. (Optional) Wait for Finalization - -The sender can wait for transaction finalization on the destination chain using the library [@layerzerolabs/scan-client](https://www.npmjs.com/package/@layerzerolabs/scan-client#example-usage). - -```typescript -const deliveredMsg = await waitForMessageReceived( - Number(lzEndpointIdOnDestChain), - sendTxReceipt?.hash as string, -); -console.log("sendOFT - received tx on destination chain:", deliveredMsg?.dstTxHash); -``` - -### Send the OFT-wrapped Tokens Back - -To send back the OFT-wrapped tokens on the destination chain to the source chain, the procedure is similar, except that the approval step is not needed: - -#### 1. Estimate the fee - -The sender calls the function `quoteSend()` of the OFT contract to estimate the cross-chain fee to be paid in native tokens on the sender chain. - -```typescript - // Set the send param - // https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/oapp/contracts/oft/interfaces/IOFT.sol#L10 - const sendParam = [ - lzEndpointIdOnSrcChain, // Sepolia - receiverAddressInBytes32, - amountInWei, - amountInWei, - options, // additional options - "0x", // composed message for the send() operation - "0x", // OFT command to be executed, unused in default OFT implementations - ]; - - // Step 1: call the func quoteSend() to estimate cross-chain fee to be paid in native on the source chain - // https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/oapp/contracts/oft/interfaces/IOFT.sol#L127C60-L127C73 - // false is set for _payInLzToken Flag indicating whether the caller is paying in the LZ token - const [nativeFee] = await myOFTContract.quoteSend(sendParam as any, false); - console.log("sendOFTBack - estimated nativeFee:", ethers.formatEther(nativeFee)); -``` - -#### 2. Send the tokens - -The sender calls the function `send()` of the OFT contract to transfer tokens from the destination chain back to the source chain. - -```typescript -const sendTx = await myOFTContract.send( - sendParam as any, - [nativeFee, 0] as any, // set 0 for lzTokenFee - sender.address, // refund address - { - value: nativeFee, - }, - ); - const sendTxReceipt = await sendTx.wait(); - console.log("sendOFTBack - send tx on source chain:", sendTxReceipt?.hash); -``` - -#### 3. (Optional) Wait for Finalization - -The sender can wait for transaction finalization on the destination chain using the library `@layerzerolabs/scan-client`. - -```typescript -const deliveredMsg = await waitForMessageReceived( - Number(lzEndpointIdOnDestChain), - sendTxReceipt?.hash as string, - ); - console.log("sendOFTBack - received tx on destination chain:", deliveredMsg?.dstTxHash); -``` - -## Sample Solidity Code for OFT Adapter and OFT Contracts in the `contracts-standard` Folder - -The [contracts-standard](https://github.com/iota-community/layerzero-oft-v2-utils/tree/main/contracts-standard) contains scripts to: - -- [Deploy the OFT Adapter and OFT contracts](#deploy-the-oft-adapter-contract-on-the-source-chain). -- [Set your trusted peers](#optional-set-the-trusted-peers). -- Set enforced options. -- [Send tokens from the source chain to the destination chain](#send-the-origin-tokens-from-the-source-chain-to-the-destination-chain), - and [vice versa](#send-oft-wrapped-tokens-back-from-the-destination-chain-to-the-origin-chain). - -### Install the Library - -After you have cloned the [IOTA Community Utilities for LayerZero OFT V2 repository](https://github.com/iota-community/layerzero-oft-v2-utils/tree/main), you should run the following command to install: - -``` -yarn -``` - -### Compile the Contracts - -If you want to use the standard implementation for ERC20, copy the [`contracts-standard`](https://github.com/iota-community/layerzero-oft-v2-utils/tree/main/contracts-standard) folder to `contracts`. If you want to use a custom implementation, copy the [`contracts-wiota`](https://github.com/iota-community/layerzero-oft-v2-utils/tree/main/contracts-wiota) to `contracts`. Then, run the following command to compile the contracts: - -```bash -yarn compile -``` - -### Set Your Configuration - -You should copy the template [`.env.example`](https://github.com/iota-community/layerzero-oft-v2-utils/blob/main/.env.example) file to a file called `.env`, and edit any of the configuration options you see fit. - -```bash -cp .env.example .env -``` - -### Deploy the Contracts - -#### Deploy the OFT Adapter Contract On the Source Chain - -The OFT Adapter facilitates the expansion of an existing token to any supported blockchain as a native token, maintaining a unified global supply and inheriting all features of the OFT Standard. This intermediary contract manages the sending and receiving of pre-deployed tokens. - -For instance, when an ERC20 token is transferred from the source chain (Chain A), it gets locked in the OFT Adapter. Consequently, a corresponding token is minted on the destination chain (Chain B) through the paired OFT Contract. - -```bash -yarn deploy-oft-adapter-sepolia -``` - -Expected log output : - -```bash -npx hardhat run scripts/deploy_oft_adapter.ts --network sepolia -Deployed MyOFTAdapter contract address: 0x4daa81978576cB91a2e1919960e90e46c2a6D586 -Done in 6.67s. -``` - -#### Deploy OFT on the Destination Chain - -You can use the following command to deploy OFT on destination chain (e.g. BNB Testnet): - -```bash -yarn deploy-oft-bnb-testnet -``` - -Expected log output : - -```bash -npx hardhat run scripts/deploy_oft.ts --network bnbTestnet -Deployed MyOFT contract address: 0xCc337C2e69F4Eb8EaBcf632a1fC5B8F729dC47F1 -Done in 6.68s. -``` - -### (optional) Set the Trusted Peers - -#### On OFTAdapter - -You can set the trusted peer in the source chain's OFT Adapter (e.g., Sepolia) using the following command: - -```bash -yarn set-peer-oft-adapter-sepolia -``` - -Expected log output : - -```bash -npx hardhat run scripts/set_peer_oft_adapter.ts --network sepolia -setPeerMyOFTAdapter - oftAdapterContractAddress:0x4daa81978576cB91a2e1919960e90e46c2a6D586, lzEndpointIdOnDestChain:40102, oftContractAddress:0xCc337C2e69F4Eb8EaBcf632a1fC5B8F729dC47F1 -MyOFTAdapter - setPeer tx: 0xc17e7a54d96325768b6427ce893d9b6b7ed04bd920089b63a3f96c005073e9c2 -Done in 14.10s. -``` - -#### On OFT - -You can add a trusted peer in the destination chain (e.g. BNB Testnet) using the following command: - -```bash -yarn set-peer-oft-bnb-testnet -``` - -Expected log output : - -```bash -npx hardhat run scripts/set_peer_oft.ts --network bnbTestnet -setPeerMyOFT - oftContractAddress:0xCc337C2e69F4Eb8EaBcf632a1fC5B8F729dC47F1, lzEndpointIdOnSrcChain:40161, oftAdapterContractAddress:0x4daa81978576cB91a2e1919960e90e46c2a6D586 -MyOFT - setPeer tx: 0xb0012378ee14c9df5c9f86980dd9c96fc8aedb3c19d92c1d91a4259f3981ac35 -Done in 4.66s. -``` - -### Send the Origin Tokens from the Source Chain to the Destination Chain - -You can use the following command to send tokens from the source chain to the destination chain: - -```bash -yarn send-oft-from-sepolia -``` - -Expected log output : - -```bash -npx hardhat run scripts/send_oft.ts --network sepolia -sendOFT - oftAdapterContractAddress:0x5D7Cbc05fc6df2832c40023f1Eb2755628C51D81, oftContractAddress:0x075e512E25b45a3EaF8b432220F0Ca8D4e3c6a58, lzEndpointIdOnSrcChain:40161, lzEndpointIdOnDestChain:40102, gasDropInWeiOnDestChain:1000000000000000, executorLzReceiveOptionMaxGas:200000, receivingAccountAddress:0x5e812d3128D8fD7CEac08CEca1Cd879E76a6E028, sender: 0x57a4bd139fb673d364a6f12df9177a3f686625f3, amount:2 -sendOFT - approve tx: 0x8fa692edb47b1ad9d21f60b0fa30993e5cd3abd78c3c56fb4f38db5f9b8ac369 -sendOFT - estimated nativeFee: 0.000734209489447653 -sendOFT - send tx on source chain: 0xeb3e44310a09ae2ab2f0d6d6d3fdfd7c490f8ac536bb20a5e16999b23232ef67 -Wait for cross-chain tx finalization by LayerZero ... -sendOFT - received tx on destination chain: 0xc2e5a4be8ae67718e817ff585a32765e393835880068f408fd7724667a25a46c -``` - -### Send Oft-Wrapped Tokens Back From the Destination Chain to the Origin Chain - -You can use the following command to send the OFT-wrapped tokens back to the origin chain: - -```bash -yarn send-oft-back-from-bnb-testnet -``` - -Expected log output : - -```bash -npx hardhat run scripts/send_oft_back.ts --network bnbTestnet -sendOFTBack - oftAdapterContractAddress:0x5D7Cbc05fc6df2832c40023f1Eb2755628C51D81, oftContractAddress:0x075e512E25b45a3EaF8b432220F0Ca8D4e3c6a58, lzEndpointIdOnSrcChain:40161, lzEndpointIdOnDestChain:40102, gasDropInWeiOnDestChain:1000000000000000, executorLzReceiveOptionMaxGas:200000, receivingAccountAddress:0x57A4bD139Fb673D364A6f12Df9177A3f686625F3, sender: 0x5e812d3128D8fD7CEac08CEca1Cd879E76a6E028, amount:2 -sendOFTBack - estimated nativeFee: 0.054815809525020364 -sendOFTBack - send tx on source chain: 0x41bcf78b310dc1bbf9b4005f7412d995011c7815ad5af9cc26b37370e75bbfeb -Wait for cross-chain tx finalization by LayerZero ... -sendOFTBack - received tx on destination chain: 0xc1031694e92512a0189885ad6419e33196a65b8ae56baa9d555be8686d6d42fe -``` diff --git a/docs/content/developer/iota-evm/how-tos/send-NFTs-across-chains.md b/docs/content/developer/iota-evm/how-tos/send-NFTs-across-chains.md deleted file mode 100644 index c5a5ada045c..00000000000 --- a/docs/content/developer/iota-evm/how-tos/send-NFTs-across-chains.md +++ /dev/null @@ -1,365 +0,0 @@ -# Send NFTs Across Chains - -## Introduction - -[LayerZero ONFT V1FV](https://docs.layerzero.network/V1) enables cross-chain transfers of existing ERC721 tokens. For -testing purposes, the ShimmerEVM Testnet is chosen as the source chain, while the BNB Testnet is chosen as the destination -chain. - -:::info Community Libs - -You can clone the Utilities for LayerZero ONFT V1 -from [IOTA Community GitHub Repository](https://github.com/iota-community/layerzero-onft-v1-utils). - -::: - -## Why Would a User Need to Send ERC721 Tokens Across Chains? - -By facilitating the movement of ERC721 tokens across chains, users gain flexibility and can optimize their NFT usage -according to their specific needs, preferences, and circumstances. - -### Enable the Existing ERC721 Tokens for Cross-Chain Sending - -To enable the existing ERC721 tokens for cross-chain sending, you will need the `ProxyONFT` contract on the source -chain, -and the ONFT contract on the destination chain) are needed. - -The origin NFT token will be locked in the `ProxyONFT` contract so that the ONFT-wrapped tokens will be minted on the -destination chain. If the NFT token already exists on the destination chain (i.e., when the ONFT-wrapped token on -the destination chain is sent back to the source chain), no new token minting will happen. Instead, the NFT tokens will be -transferred from the ONFT contract to the user's wallet address. Relevant code - -### Enable Cross-Chain Sending for Unloached ERC721 NFTs - -If you are launching a new ERC721 token, you can use the ONFT standard to enable cross-chain sending without the need of -`ProxyONFT`. As with existing tokens, the NFT will be locked on the source chain and minted or transferred on the destination chain. - -:::info Contract Documentation - -- [ProxyONFT721](https://docs.layerzero.network/v1/developers/evm-guides/contract-standards/721#proxyonft721sol) -- [ProxyONFT1155](https://docs.layerzero.network/v1/developers/evm-guides/contract-standards/1155#proxyonft1155sol) -- [ONFT721](https://docs.layerzero.network/v1/developers/evm-guides/contract-standards/721#onft721sol) -- [ONFT1155](https://docs.layerzero.network/v1/developers/evm-guides/contract-standards/1155#onft1155sol) - -::: - -## Scripts - -### Deploy the ProxyONFT and ONFT Contracts - -#### For ERC721 - -- MyProxyONFT721.sol: - - CTOR: - - [`minGasToTransferAndStore`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/token/onft721/ONFT721Core.sol#L169): - The minimum gas needed to transfer and store your NFT is typically 100k for ERC721. This value would vary - depending on your contract complexity; it's recommended to test. If this value is set too low, the destination - tx will fail, and a manual retry is needed. - - `lzEndpoint`: LayerZero Endpoint on the source chain. - - `proxyToken`: deployed contract address of the NFT tokens on the source chain. - -- MyONFT721.sol: - - CTOR: - - `name`: name of the ONFT-wrapped tokens on the destination chain - - `symbol`: symbol of the ONFT-wrapped tokens on the destination chain - - [`minGasToTransferAndStore`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/token/onft721/ONFT721Core.sol#L169): - The minimum gas needed to transfer and store your NFT typically 100k for ERC721. This value would vary - depending on your contract complexity; it's recommended to test. If this value is set too low, the destination - tx will fail, and a manual retry is needed. - - `lzEndpoint`: - lzEndpoint: LayerZero Endpoint on the destination chain - -### Set the Trusted Remote - -For **existing ERC721 tokens**, the `ProxyONFT` and `ONFT` contract instances must be paired. - -For the **upcoming ERC721 tokens** that want to leverage the `ONFT` standard, the `ONFT` contract instance on the source chain -needs to be paired with another `ONFT` contract instance on the destination chain. - -You can set this using the [`setTrustedRemote`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/LzApp.sol#L138) method. - -### Set the `minGasLimit` - -Both the `ProxyONFT` and the `ONFT` contract instanceS need to be set for minimum gas on destination([`minGasLimit`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/LzApp.sol#L85C37-L85C48)). - -You can set his using the [`setMinDstGas`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/LzApp.sol#L159) method. - -:::info - -It is required that `minDstGas` <= `providedGasLimit`, which is to be set via `adapterParams` upon cross-chain sending on -the source chain. - -::: - -### Set the Batch Size Limit - -Both the `ProxyONFT` and the `ONFT` contract instances need to set a limit for the batch size on the source -chain to limit the number of tokens to be sent to another chain when using the -[`sendBatchFrom`](https://github.com/LayerZero-Labs/solidity-examples/blob/c04e7d211b1b610f84761df943e6a38b0a53d304/contracts/token/onft721/ONFT721Core.sol#L67) -method. - -You can set this using the [`setDstChainIdToBatchLimit`](https://github.com/LayerZero-Labs/solidity-examples/blob/c04e7d211b1b610f84761df943e6a38b0a53d304/contracts/token/onft721/ONFT721Core.sol#L194) method; the default value is 1. - -## How To Send Tokens From a Source Chain to a Destination Chain (and Vice-Versa) - -### Required Contracts - -#### From the Source Chain to the Destination Chain - -For the existing ERC721 tokens, you will need the `ProxyONFT` contract on the source chain and the `ONFT` contract on -the destination chain. The procedure is as follows: - -1. The sender approves his ERC721 tokens for the `ProxyONFT` contract. -2. The sender calls the function [`estimateSendFee()`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/token/onft721/interfaces/IONFT721Core.sol#L70) of the ProxyONFT contract to estimate cross-chain fee to be paid in - native on the source chain. -3. The sender calls the function [`sendFrom()`](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/token/onft721/interfaces/IONFT721Core.sol#L36) of the ProxyONFT contract to transfer tokens on source chain to destination - chain. -4. (Optional) Wait for the transaction finalization on the destination chain by using the - [@layerzerolabs/scan-client](https://www.npmjs.com/package/@layerzerolabs/scan-client#example-usage) library. - -#### From the Destination Chain Back to the Source Chain - -To send back the ONFT-wrapped tokens on the destination chain to the source chain, the procedure is similar as the -approve step is also required, but the operations will happen on the `ONFT` contract. - -#### References and Tools - -##### `AdapterParams` - -- You can use the [LayerZero Repository](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/libs/LzLib.sol#L44) as a reference to set gas drop on the destination in `adapterParams`. - - The provided gas drop must be `<=` the config one. Otherwise, you will get [`dstNativeAmt` too large](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/mocks/LZEndpointMock.sol#L413) error. -- You can use the [LayerZero Repository](https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/lzApp/libs/LzLib.sol#L34) as a reference to set default `adapterParams` without needing a gas drop. - -##### LayerZero - -- [LayerZero Endpoint V1](https://docs.layerzero.network/v1/developers/technical-reference/mainnet/mainnet-addresses) -- [LayerZero explorer](https://Testnet.layerzeroscan.com/) - -### Install the Library - -After you have cloned -the [IOTA Community Utilities for LayerZero ONFT V2 repository](https://github.com/iota-community/layerzero-onft-v1-utils), -you should run the following command to install: - -```bash -yarn -``` - -### Compile the Contracts - -If you want to use the standard implementation for ERC20, copy -the [`contracts-standard`](https://github.com/iota-community/layerzero-oft-V1-utils/tree/main/contracts-standard) folder -to `contracts`. If you want to use a custom implementation, copy -the [`contracts-wiota`](https://github.com/iota-community/layerzero-oft-V1-utils/tree/main/contracts-wiota) -to `contracts`. Then, run the following command to compile the contracts: - -```bash -yarn compile -``` - -### Set Your Configuration - -You should copy the -template [`.env.example`](https://github.com/iota-community/layerzero-oft-V1-utils/blob/main/.env.example) file to a -file called `.env`, and edit any of the configuration options you see fit. - -```bash -cp .env.example .env -``` - -### Deploy the Contracts - -#### Deploy a mock ERC721 - -```bash -yarn deploy-ERC721-mock-smr-Testnet -``` - -Expected log output : - -```bash -npx hardhat run scripts/deploy_ERC721.ts --network shimmerEvmTestnet -Deployed ERC721Mock contract address:0xFddbA8928a763679fb8C99d12541B7c6177e9c3c -Done in 4.49s. -``` - -#### Deploy `ProxyONFT721` on the source chain - -You can use the following command to deploy ProxyONFT721 on the source chain (e.g., ShimmerEVM Testnet): - -```bash -yarn deploy-proxy-onft-smr-Testnet -``` - -Expected log output : - -```bash -npx hardhat run scripts/deploy_proxy_onft721.ts --network shimmerEvmTestnet -Deployed MyProxyONFT721 contract address:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2 -Done in 4.50s. -``` - -#### Deploy `ProxyONFT721` on the destination chain - -You can use the following command to deploy ProxyONFT721 on the destination chain (e.g., BNB Testnet): - -```bash -yarn set-min-dest-gas-onft-bnb-Testnet -``` - -Expected log output : - -```bash -export isForProxy=false && npx hardhat run scripts/set_min_destination_gas.ts --network bnbTestnet -setMinDstGas - isForProxy:false, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10230, minDstGas:150000 -setMinDstGas (packetType 0) tx: 0xce044ded17daa77a8aefc3d39b99c5381216eb4057ddce6253affde6cda2091c -setMinDstGas (packetType 1) tx: 0x3a26ae40ac058099bfd8b85910009a5e5e8b03f16a5f032b572827d48be8f2b0 -Done in 9.34s. -``` - -### Set the Minimum Destination Gas - -#### On the source chain - -You can use the following command to set the minimum destination gas on the `ProxyONFT` contract on the source chain (e.g., ShimmerEVM Testnet): - -```bash -yarn set-min-dest-gas-proxy-onft-smr-Testnet -``` - -Expected log output : - -```bash -export isForProxy=true && npx hardhat run scripts/set_min_destination_gas.ts --network shimmerEvmTestnet -setMinDstGas - isForProxy:true, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10102, minDstGas:150000 -setMinDstGas (packetType 0) tx: 0xcab06e9989448153a4bbc1bb166fc2d33467f3311d1851bf2ff719d982daa613 -setMinDstGas (packetType 1) tx: 0xe78fd3f0bf668fafbc423decd2cf14a27d74543af3ac9daf031f0b278c22ea78 -Done in 6.07s. -``` - -#### On the destination chain - -You can use the following command to set the minimum destination gas on the `ONFT` contract on the destination chain (e.g., BNB Testnet): - -```bash -yarn set-min-dest-gas-onft-bnb-Testnet -``` - -Expected log output : - -```bash -export isForProxy=false && npx hardhat run scripts/set_min_destination_gas.ts --network bnbTestnet -setMinDstGas - isForProxy:false, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10230, minDstGas:150000 -setMinDstGas (packetType 0) tx: 0xce044ded17daa77a8aefc3d39b99c5381216eb4057ddce6253affde6cda2091c -setMinDstGas (packetType 1) tx: 0x3a26ae40ac058099bfd8b85910009a5e5e8b03f16a5f032b572827d48be8f2b0 -Done in 9.34s. -``` - -### Set the batch size limit - -#### On the source chain - -You can use the following command to set batch size limits on the `ProxyONFT` contract on the source chain (e.g., ShimmerEVM Testnet): - -```bash -yarn set-batch-size-limit-proxy-onft-smr-Testnet -``` - -Expected log output : - -```bash -export isForProxy=true && npx hardhat run scripts/set_batch_size_limit.ts --network shimmerEvmTestnet -setBatchSizeLimit - isForProxy:true, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10102, batchSizeLimit:1 -setBatchSizeLimit tx: 0x70c23b3d3d5e94ef82e50944f7eba93fa1fe8db3a5487ac371015e7a14482e75 -Done in 4.28s. -``` - -#### On the destination chain - -You can use the following command to set batch size limits on the `ONFT` contract on the destination chain (e.g., BNB Testnet): - -```bash -yarn set-batch-size-limit-onft-bnb-Testnet -``` - -Expected log output : - -```bash -export isForProxy=false && npx hardhat run scripts/set_batch_size_limit.ts --network bnbTestnet -setBatchSizeLimit - isForProxy:false, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10230, batchSizeLimit:1 -setBatchSizeLimit tx: 0x8cb44c2195ac93da552c646677e6585c95ab172df19463637541933ec70dc9b8 -Done in 4.26s. -``` - -### Set the Trusted Remote - -#### On the source chain - -You can use the following command to set a trusted remote on the `ProxyONFT` contract on the source chain (e.g., ShimmerEVM Testnet): - -```bash -yarn set-remote-proxy-onft-smr-Testnet -``` - -Expected log output : - -```bash -export isForProxy=true && npx hardhat run scripts/set_trusted_remote.ts --network shimmerEvmTestnet -setTrustedRemote - isForProxy:true, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10102 -setTrustedRemote tx: 0xce52c0f25090ef7c1668ef04ff2f6098551c9f56b3ce881d17181bf106457016 -Done in 4.24s. -``` - -##### On the destination chain - -You can use the following command to set a trusted remote on the `ONFT` contract on the destination chain (e.g., BNB Testnet): - -```bash -yarn set-remote-onft-bnb-Testnet -``` - -Expected log output : - -```bash -export isForProxy=false && npx hardhat run scripts/set_trusted_remote.ts --network bnbTestnet -setTrustedRemote - isForProxy:false, proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnRemoteChain:10230 -setTrustedRemote tx: 0x311a0568b5afce7d601df2613f8ff80428d8a4d2f2c91012e0e4a8cbc0aedf59 -Done in 4.88s. -``` - -### Send Origin Tokens From the Source Chain To the Destination Chain - -```bash -yarn send-onft-from-smr-Testnet -``` - -Expected log output: - -```bash -npx hardhat run scripts/send_onft.ts --network shimmerEvmTestnet -sendONFT - proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnSrcChain:10230, lzEndpointIdOnDestChain:10102, gasDropInWeiOnDestChain:0, providedGasLimit:200000, receivingAccountAddress:0x5e812d3128D8fD7CEac08CEca1Cd879E76a6E028, sender: 0x57A4bD139Fb673D364A6f12Df9177A3f686625F3, nftTokenId:2, nftTokenAddress:0xFddbA8928a763679fb8C99d12541B7c6177e9c3c -sendONFT - approve tx: 0xa871bc79e45bf20f33c626044d6e208460c5745ab1f13d476dcbe04e1da7e592 -sendONFT - estimated nativeFee: 158.319172348046094655 -sendONFT - send tx on source chain: 0x72779c7549053194e42bcc78f78cf65e876867f0516dc91f28986c854e652596 -Wait for cross-chain tx finalization by LayerZero ... -sendONFT - received tx on destination chain: 0x2700a9d35c139eb84ba07b75490e6627a30e00bde130e3cb7c1cbb81c0326138 -Done in 53.50s. -``` - -### Send ONFT-Wrapped Tokens Back From the Destination Chain Back To the Origin Chain - -```bash -yarn send-onft-back-from-bnb-Testnet -``` - -Expected log output: - -```bash -npx hardhat run scripts/send_onft_back.ts --network bnbTestnet -sendONFTBack - proxyONFTContractAddress:0x7B0D46219C915e7Ff503C7F83a805c0b2F4ab2F2, onftContractAddress:0xC617A0Bd9DC6093a304515d3dbFF4244333fDeBB, lzEndpointIdOnSrcChain:10230, lzEndpointIdOnDestChain:10102, gasDropInWeiOnDestChain:0, providedGasLimit:200000, receivingAccountAddress:0x57A4bD139Fb673D364A6f12Df9177A3f686625F3, sender: 0x60917645A28258a75836aF63633850c5F3561C1b, nftTokenId:2, nftTokenAddress:0xFddbA8928a763679fb8C99d12541B7c6177e9c3c -sendONFTBack - approve tx: 0xe5bfff108528efdc67e72896845f0ad3e0186b4ed64835e7c5f3552eaab69d99 -sendONFTBack - estimated nativeFee: 0.000498452810033053 -sendONFTBack - send tx on source chain: 0xa43bb5547a5a35730fe183b4d554416a4ea34852e510d21f24d173db75db4e79 -Wait for cross-chain tx finalization by LayerZero ... -sendONFTBack - received tx on destination chain: 0xb05fa2de194153819b26d17893278c485abbaf355fa24f26fbc7a4c759994cde -Done in 212.16s. -``` diff --git a/docs/content/developer/iota-evm/how-tos/send-funds-from-L1-to-L2.mdx b/docs/content/developer/iota-evm/how-tos/send-funds-from-L1-to-L2.mdx deleted file mode 100644 index 1365f2e26ca..00000000000 --- a/docs/content/developer/iota-evm/how-tos/send-funds-from-L1-to-L2.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -description: How to send funds from L1 to L2. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - rpc - - how-to ---- - -import { AddToMetaMaskButton } from '@site/src/components/AddToMetaMaskButton'; -import MetamaskButtons from '../../../_snippets/iota-evm/metamask_buttons.mdx'; -import { Networks } from '@site/src/components/constant'; - -# Send Funds From L1 to L2 - -There are multiple types of assets available on both IOTA L1 and L2, here we discuss a few of them, and how to get them -onto L2. - -:::tip Testnet - -If you want to fund your EVM Testnet account using the EVM Toolkit, please refer to our [Testnet Quickstart Guide](../getting-started/quick-start.mdx) - -::: - -## Fund an Ethereum Account on a IOTA Smart Contracts Chain - -To send EVM transactions, you need to have an Ethereum address that owns tokens on the ISC chain (L2). These tokens will -be used to cover gas fees. - - - - -You can use your [Firefly Wallet](https://firefly.iota.org/) to easily send L1 IOTA or SMR to your L2 IOTA EVM or ShimmerEVM account. - -#### Requirements - -- IOTA Tokens or Shimmer Tokens -- [Firefly Wallet](https://firefly.iota.org/) -- [Metamask](https://metamask.io/) - -1. The first thing you will need to do is add the EVM to Metamask by hitting the following button. - - - -2. Once you have added the EVM to Metamask, you can get your address: - - ![Copy your Metamask address](/img/iota-evm/evm/how-tos/get-funds/copy-your-address.png) - -3. Next, you will need to open your [Firefly Wallet](https://firefly.iota.org/) and click on `Send Assets`. - - ![Click send assets](/img/iota-evm/evm/how-tos/get-funds/firefly/select-send-assets.png) - -4. Select the EVM chain you want to use in the network dropdown. - - ![Select the EVM network](/img/iota-evm/evm/how-tos/get-funds/firefly/select-shimmer-evm.png) - -5. Enter the amount of tokens you want to transfer, and the Metamask address from step 2, and click on `Next` - - ![Enter the amount of tokens and metamask address](/img/iota-evm/evm/how-tos/get-funds/firefly/enter-your-desired-amount-and-metamask-address.png) - -6. Review the transaction details and click on `Send`. - - ![Hit Send](/img/iota-evm/evm/how-tos/get-funds/firefly/hit-send.png) - - - - -You can use your [Bloom Wallet](https://bloomwallet.io/) to easily send L1 base token to your L2 EVM account. - -1. First, you will need to open your [Bloom Wallet](https://firefly.iota.org/) and click on `Send`. - - ![Click send](/img/iota-evm/evm/how-tos/get-funds/bloom/select-send.png) - -2. Select an account with base tokens. - - ![Select an account with base tokens](/img/iota-evm/evm/how-tos/get-funds/bloom/select-the-smr-token.png) - -3. Bloom will automatically create an EVM address for you, so you can send funds to that address from the - EVM dropdown. Alternatively, you can input any other EVM address. - - ![Select you EVM Address](/img/iota-evm/evm/how-tos/get-funds/bloom/enter-the-recipient-address.png) - -4. Enter the amount of base tokens you want to transfer. - - ![Enter the amount of base tokens you want to transfer](/img/iota-evm/evm/how-tos/get-funds/bloom/enter-the-amount.png) - -5. Review the transaction details and click on `Confirm`. - - ![Hit Send](/img/iota-evm/evm/how-tos/get-funds/bloom/review-and-confirm-the-transaction.png) - - - diff --git a/docs/content/developer/iota-evm/how-tos/test-smart-contracts.md b/docs/content/developer/iota-evm/how-tos/test-smart-contracts.md deleted file mode 100644 index ff0f8d17d8c..00000000000 --- a/docs/content/developer/iota-evm/how-tos/test-smart-contracts.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -description: Learn how to test smart contracts before deploying them on public networks to avoid vulnerabilities and ensure functionality aligns with requirements using unit, and integration testing, alongside frameworks and testing with the IOTA Sandbox and the EVM Testnet. -tags: - - evm ---- - -# Testing Smart Contracts - -Once you have deployed a smart contract on a public network, altering it to fix an error can be hard, and it goes -against the principle of immutability, which can create trust issues with users. And, even if it were simple, you could -only fix an error or vulnerability after it is discovered. So, your contract and its users may be at risk if a third -party detects a vulnerability before you do. - -The best way to avoid these overheads and vulnerabilities is to test your contracts thoroughly before deploying them. -You can use [unit](#unit-tests) and [integration](#integration-tests) tests, as well -as [manual testing](#manual-testing), to ensure your contracts behave as expected. - -## Automated Testing - -You can use various tools to automatically test your smart contract’s code and for any error in execution. The great -benefit of automated testing is that it requires minimal human intervention, and it can be built right into your -deployment pipelines. - -Automated testing also allows you to run repetitive and time-consuming tasks without testing each case manually and -avoid human errors, which can happen when you manually input tons of data. - -However, you must be careful, as automated testing can lead to false positives or miss certain edge cases. - -### Unit Tests - -In a nutshell, unit testing ensures that each function and component works correctly by evaluating each independently. -You can use unit tests to ensure that functions return the expected values and modify your contract's state properly. -They should be simple, quick to run and provide helpful feedback if anything goes wrong. This makes them an ideal tool -to ensure your contracts run smoothly after any modifications. - -#### Unit Testing Guidelines - -##### Understand the logic behind your contract’s workflow - -Before you can write any tests, you should understand how your contract is supposed to behave. You should know its -functionalities and how users will access them. Your tests should cover different functions a user may call when -interacting with the contract and check that functions are disabled when they should be. - -#### Evaluate your contract’s assumptions - -It is important to verify that every internal decision the contract takes matches your assumptions on how it should -behave. This forces you to think about edge cases rather than only thinking of the “happy path” you want your users to -take. You can write negative tests to assert how your contract would respond to invalid or malicious inputs. - -##### Measure Code Coverage - -Code coverage is a key metric when it comes to understanding the effectiveness of your tests. It measures the number of -lines, statements, and branches that are actually executed during your unit tests. If you don’t have thorough code -coverage, your test may give what is commonly known as a “false negative”, where your contract passed all your tests, -but your tests didn’t evaluate all the possible vulnerabilities. - -#### Unit Testing Frameworks - -The quality of your unit test will depend on the quality of the tools you use to write and execute them. You should use -a testing framework that is regularly maintained and updated, provides features that are relevant to your contract, and -is popular amongst other developers. - -:::tip Solo - -If you want to test ISC-specific functionalities, like the [magic contract](./core-contracts/introduction.md), you should use -the [Solo Framework](../solo/getting-started.md). - -::: - -- [Waffle](https://ethereum-waffle.readthedocs.io/en/latest/getting-started.html#writing-tests) -- [Remix](https://remix-ide.readthedocs.io/en/latest/unittesting.html#write-tests) -- [Hardhat](https://hardhat.org/hardhat-runner/docs/guides/test-contracts) -- [Brownie](https://eth-brownie.readthedocs.io/en/v1.0.0_a/tests.html) -- [Ape](https://docs.apeworx.io/ape/stable/userguides/testing.html) -- [Foundry](https://book.getfoundry.sh/forge/writing-tests) - -### Integration Tests - -Integration tests are beneficial when your contract adopts a modular architecture or interfaces with other contracts -during its execution. While [unit tests](#unit-tests) focus on testing functions in isolation, trying each cog in the -machine individually, so to speak, integration tests evaluate how these functions work together as a whole, testing the -machine as a whole. You should use integration tests to detect any issues arising from interactions between different -functions in your contracts' cross-contract calls and ensure that inherited or extended functions are working as -expected. - -#### Tools - -You can use the EVM Testnet to conduct integration tests without -incurring any fees or the IOTA Sandbox if you want to run the tests locally. - -## Manual Testing - -Once you have a complete batch of [automated tests](#automated-testing), manually testing your contract to ensure it -behaves as expected in the real world is still good practice. However, to avoid incurring fees or deploying a faulty -contract, you can manually test your contract using a sandboxed local network and the EVM Testnet. - -Testing using the IOTA Sandbox serves well for the first stage of automated and manual -integration tests, as you have complete control over the entire local network. Once you are confident about how your -contract behaves locally, you can deploy and test on the EVM Testnet, -which replicates the IOTA EVM and ShimmerEVM networks, but also enables cost and risk-free interactions. diff --git a/docs/content/developer/iota-evm/introduction.mdx b/docs/content/developer/iota-evm/introduction.mdx deleted file mode 100644 index a5e98e7f9c8..00000000000 --- a/docs/content/developer/iota-evm/introduction.mdx +++ /dev/null @@ -1,70 +0,0 @@ ---- -description: The current release of IOTA Smart Contracts also has experimental support for EVM/Solidity, providing limited compatibility with existing smart contracts and tooling from other EVM based chains like Ethereum. -image: /img/logo/WASP_logo_dark.png -tags: - - evm - - solidity - - explanation ---- - -import OnOffLedgerRequest from '../../_snippets/iota-evm/on_off_ledger_request.md'; - -# Introduction - -Smart contracts are deterministic applications that run on a distributed network with multiple -[validators](explanations/validators.md) that execute and validate the same code. -Their deterministic and distributed nature makes them predictable, stable and trustworthy. - -## Scalable Smart Contracts - -Due to the distributed nature of smart contracts, i.e. they run on a network of validators instead of a single computer, -the execution of smart contract is resource intensive as it has to deal with the overhead of the communication between validators in the network. -This can lead to relatively high [fees](#gas) for smart contract execution, as well as scalability issues when running on -a single blockchain. However, the IOTA Smart Contract Protocol allows **many blockchains that execute smart contracts to -run in parallel** and communicate with one another, therefore solving the scalability problem. Enabling interoperability and horizontal scaling of dApps. - -At the same time, ISC provides advanced means of communication between its chains and preserves the ability to create -complex, composed smart contracts. - -## ISC Architecture - -IOTA Smart Contracts (ISC) function as a Layer 2 extension to the IOTA Multi-Asset Ledger. -As validator nodes execute the smart contracts, they tally these state changes and write them into the chain. -In turn ISC chains, each with their state and smart contracts, update their state collectively and interact with Layer 1 -and other L2 chains, offering a sophisticated multi-chain architecture. - -![IOTA Smart Contacts multichain architecture](/img/iota-evm/multichain.png 'Click to see the full-size image.') - -_IOTA Smart Contacts multichain architecture._ - -[Explore the comprehensive overview of IOTA Smart Contracts in the ISC white paper](https://files.iota.org/papers/ISC_WP_Nov_10_2021.pdf). - -## Supported VMs - -The IOTA Smart Contracts currently -supports [EVM/Solidity](getting-started/languages-and-vms.mdx#what-is-evmsolidity) -smart contracts, as well as an **experimental** [Wasm VM](getting-started/languages-and-vms.mdx#wasm-vm-for-isc). - -## Sandbox Interface - -ISC Smart contracts can access the [Sandbox interface](explanations/sandbox.md). -This interface provides access to the chain state, native assets, allows interaction with other contracts/chains, as -well as various utilities like cryptographic functions and event dispatching. - -![Sandbox](/img/iota-evm/sandbox.png) - -## Calling a Smart Contract - -### Entry Points and Requests - -Smart contracts are activated through entry points, similar to function calls. Entry points can be view-only or allow state -modifications. They are triggered by requests, signed by senders. Smart contracts on the same chain can -synchronously invoke each other, ensuring deterministic results. However, requests between chains are asynchronous and -may involve delays. - -### Gas - -Running a request consumes 'gas'. Gas units are a measurement of "how expensive" a computation is to execute. You can specify a `GasBudget` -for each request, with costs charged to your on-chain account. - - diff --git a/docs/content/developer/iota-evm/schema/how-tos/access.mdx b/docs/content/developer/iota-evm/schema/how-tos/access.mdx deleted file mode 100644 index 24c77f3d3e7..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/access.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: This article explains how to define access controls for functions using the optional 'access' subsection in the schema definition file. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Limit Access - -You can define function access controls through the optional `access` subsection in the schema definition file. -By default, functions are accessible to everyone. However, when specified, the `access` identifier restricts who can -call a function. - -## Access Identifiers - -You can set access identifiers with **one** of the following options: - -- `self`: Restricts access to the smart contract itself. -- `chain`: Allows only the chain owner to invoke the function. -- Other: Specify an AgentID or AgentID[] variable in state storage, controlling the agent(s) permitted to call the - function. - You should provide a mechanism to initialize and manage this variable. - -The [Schema Tool](usage.mdx) handles the auto-generation of the code for access verification. - -## Usage Examples - -You can find examples in the schema definition file where the `owner` state variable operates as an access identifier. -Functions such as `init` initialize the state variable, while `setOwner` can modify ownership, -enhancing the security by limiting function access to the current owner. - -Remember to tailor access identifiers to individual functions as required, -and establish multiple identifiers if necessary. diff --git a/docs/content/developer/iota-evm/schema/how-tos/call.mdx b/docs/content/developer/iota-evm/schema/how-tos/call.mdx deleted file mode 100644 index 0644bb1ff98..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/call.mdx +++ /dev/null @@ -1,172 +0,0 @@ ---- -description: Explore how synchronous function calls work between smart contracts, highlighting the role of function descriptors in parameter and token passage, and understanding the ISC host's role in this procedure. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Call Functions - -Synchronous function calls between smart contracts act very similar to how normal function calls work in any programming language, -but with a slight twist. -With normal function calls, you share all the global memory that you can access with every function that you call. -However, when calling a smart contract function, -you can only access the memory assigned to that specific smart contract. - -## How Synchronous Calls Operate - -### Data Sharing - -If you want to share data between smart contracts, you will need to use: - -- Function [parameters](params.mdx) -- [Return](results.mdx) values - -## The ISC Host Role - -Ensuring smooth synchronous calls between contracts on the same chain, the ISC host: - -- Recognizes all contracts functioning on a chain. -- Directs the call appropriately to the destined contract function using function descriptors. - -### Function Descriptors - -Function descriptors allow: - -- Specification of call parameters via the [Params](params.mdx) proxy. -- Function invocation through the `func` interface. -- Passing tokens to non-[View](views.mdx) function calls within the same chain. - -:::note - -The only way to call a function and properly pass tokens to it within the same chain is through the function descriptor. -Otherwise, the allowance() function will not register any incoming tokens. - -::: - -### Call Process - -During a call: - -- The initiator function pauses and awaits the completion of the called function. -- Post-completion, retrieves potential returned values through the [Results](results.mdx) proxy. - -### Calling From View Functions - -When a view function initiates a call: - -- It can only reach out to other [view](views.mdx) functions. -- The `ScFuncs` interface mandates an `ScViewContext` for the constructor creating the function descriptor. - -## Usage Example - -Here's how a smart contract would tell a `dividend` contract on the same chain to divide the 1000 tokens it passes to the function: - - - - - -```go -f := dividend.ScFuncs.Divide(ctx) -f.Func.TransferBaseTokens(1000).Call() -``` - - - - -```rust -let f = dividend::ScFuncs::divide(ctx); -f.func.transfer_base_tokens(1000).call(); -``` - - - - -```ts -let f = dividend.ScFuncs.divide(ctx); -f.func.transferBaseTokens(1000).call(); -``` - - - - -And here is how a smart contract would ask a `dividend` contract on the same chain to -return the dispersion factor for a specific address: - - - - - -```go -f := dividend.ScFuncs.GetFactor(ctx) -f.Params.Address().SetValue(address) -f.Func.Call() -factor := f.Results.Factor().Value() -``` - - - - -```rust -let f = dividend::ScFuncs::get_factor(ctx); -f.params.address().set_value(&address); -f.func.call(); -let factor = f.results.factor().value(); -``` - - - - -```ts -let f = dividend.ScFuncs.getFactor(ctx); -f.params.address().setValue(address); -f.func.call(); -let factor = f.results.factor().value(); -``` - - - - -1. Create a function descriptor for the desired function. -2. Use the [Params](params.mdx) proxy in the descriptor to set its parameters. -3. Direct the `func` member of the descriptor to call the associated function -4. Use the [Results](results.mdx) proxy in the descriptor to retrieve its results. - -The function descriptors assume that the function to be called is associated with the -default Hname of the contract, in this case ScHname::new("dividend"). If you deployed the -contract that contains the function you want to call under a different name, then you -would have to provide its associated Hname to the `func` member through the of_contract() -member function like this: - - - - - -```go -altContract := NewScHname("alternateName") -f := dividend.ScFuncs.Divide(ctx) -f.Func.OfContract(altContract).TransferBaseTokens(1000).Call() -``` - - - - -```rust -let alt_contract = ScHname::new("alternateName"); -let f = dividend::ScFuncs::divide(ctx); -f.func.of_contract(alt_contract).transfer_base_tokens(1000).call(); -``` - - - - -```ts -let altContract = ScHname.fromString('alternateName'); -let f = dividend.ScFuncs.divide(ctx); -f.func.ofContract(altContract).transferBaseTokens(1000).call(); -``` - - - diff --git a/docs/content/developer/iota-evm/schema/how-tos/events.mdx b/docs/content/developer/iota-evm/schema/how-tos/events.mdx deleted file mode 100644 index ba2597c15af..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/events.mdx +++ /dev/null @@ -1,311 +0,0 @@ ---- -description: This article outlines how to trigger events in smart contracts utilizing ISC sandbox's ScFuncContext and the Schema Tool for structured events. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Trigger Events - -Smart contracts operate in a confined environment but need a mechanism to interact with users. -A solution to this is triggering events, which is facilitated through smart contracts. - -## ISC Sandbox Interface - -The `ScFuncContext` [Call Context](../../explanations/context.mdx) in ISC _Sandbox_ has an `event()` function to support event triggering. -This function takes a text string parameter, requiring creators and users to maintain and understand the chosen format. -However, this setup is prone to errors and inconsistency due to the arbitrary nature of the text strings used. - -## Structured Events with Schema Tool - -To mitigate issues stemming from the rudimentary interface, -use the [Schema Tool](usage.mdx) to define structured events, -making event creation and handling more consistent and less error-prone. - -This tool allows you to establish structured events that are integrated into all Func function contexts. -Note that events: - -- Can only be triggered within a Func -- Become part of the smart contract's state -- Are logged in the core `blocklog` contract -- Cannot be triggered within a View - -## Event Structure - -Define each event in the `events` section of the schema definition file. -This setup ensures that events are encoded consistently, -utilizing a function that automatically formats the event string with the event name, timestamp, and parameter fields, -delimited by vertical bars. -This structured approach facilitates streamlined, error-resistant event triggering. - -### Example - -Here is the `events` section that can be found in the demo `fairroulette` smart contract: - - - - -```yaml -events: - bet: - address: Address # address of better - amount: Uint64 # amount of tokens to bet - number: Uint16 # number to bet on - payout: - address: Address # address of winner - amount: Uint64 # amount of tokens won - round: - number: Uint32 # current betting round number - start: - stop: - winner: - number: Uint16 # the winning number -``` - - - - -The [Schema Tool](usage.mdx) will generate `events.xx` which contains the following code -for the `FairRouletteEvents` struct: - - - - - -```go -package fairroulette - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib" -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" - -type FairRouletteEvents struct { -} - -func (e FairRouletteEvents) Bet( - // address of better - address wasmtypes.ScAddress, - // amount of tokens to bet - amount uint64, - // number to bet on - number uint16, -) { - evt := wasmlib.NewEventEncoder("fairroulette.bet") - evt.Encode(wasmtypes.AddressToString(address)) - evt.Encode(wasmtypes.Uint64ToString(amount)) - evt.Encode(wasmtypes.Uint16ToString(number)) - evt.Emit() -} - -func (e FairRouletteEvents) Payout( - // address of winner - address wasmtypes.ScAddress, - // amount of tokens won - amount uint64, -) { - evt := wasmlib.NewEventEncoder("fairroulette.payout") - evt.Encode(wasmtypes.AddressToString(address)) - evt.Encode(wasmtypes.Uint64ToString(amount)) - evt.Emit() -} - -func (e FairRouletteEvents) Round( - // current betting round number - number uint32, -) { - evt := wasmlib.NewEventEncoder("fairroulette.round") - evt.Encode(wasmtypes.Uint32ToString(number)) - evt.Emit() -} - -func (e FairRouletteEvents) Start() { - evt := wasmlib.NewEventEncoder("fairroulette.start") - evt.Emit() -} - -func (e FairRouletteEvents) Stop() { - evt := wasmlib.NewEventEncoder("fairroulette.stop") - evt.Emit() -} - -func (e FairRouletteEvents) Winner( - // the winning number - number uint16, -) { - evt := wasmlib.NewEventEncoder("fairroulette.winner") - evt.Encode(wasmtypes.Uint16ToString(number)) - evt.Emit() -} -``` - - - - -```rust -use wasmlib::*; - -pub struct FairRouletteEvents { -} - -impl FairRouletteEvents { - - pub fn bet(&self, - // address of better - address: &ScAddress, - // amount of tokens to bet - amount: u64, - // number to bet on - number: u16, - ) { - let mut evt = EventEncoder::new("fairroulette.bet"); - evt.encode(&address_to_string(&address)); - evt.encode(&uint64_to_string(amount)); - evt.encode(&uint16_to_string(number)); - evt.emit(); - } - - pub fn payout(&self, - // address of winner - address: &ScAddress, - // amount of tokens won - amount: u64, - ) { - let mut evt = EventEncoder::new("fairroulette.payout"); - evt.encode(&address_to_string(&address)); - evt.encode(&uint64_to_string(amount)); - evt.emit(); - } - - pub fn round(&self, - // current betting round number - number: u32, - ) { - let mut evt = EventEncoder::new("fairroulette.round"); - evt.encode(&uint32_to_string(number)); - evt.emit(); - } - - pub fn start(&self) { - let mut evt = EventEncoder::new("fairroulette.start"); - evt.emit(); - } - - pub fn stop(&self) { - let mut evt = EventEncoder::new("fairroulette.stop"); - evt.emit(); - } - - pub fn winner(&self, - // the winning number - number: u16, - ) { - let mut evt = EventEncoder::new("fairroulette.winner"); - evt.encode(&uint16_to_string(number)); - evt.emit(); - } -} -``` - - - - -```ts -import * as wasmlib from 'wasmlib'; -import * as wasmtypes from 'wasmlib/wasmtypes'; - -export class FairRouletteEvents { - bet( - // address of better - address: wasmtypes.ScAddress, - // amount of tokens to bet - amount: u64, - // number to bet on - number: u16, - ): void { - const evt = new wasmlib.EventEncoder('fairroulette.bet'); - evt.encode(wasmtypes.addressToString(address)); - evt.encode(wasmtypes.uint64ToString(amount)); - evt.encode(wasmtypes.uint16ToString(number)); - evt.emit(); - } - - payout( - // address of winner - address: wasmtypes.ScAddress, - // amount of tokens won - amount: u64, - ): void { - const evt = new wasmlib.EventEncoder('fairroulette.payout'); - evt.encode(wasmtypes.addressToString(address)); - evt.encode(wasmtypes.uint64ToString(amount)); - evt.emit(); - } - - round( - // current betting round number - number: u32, - ): void { - const evt = new wasmlib.EventEncoder('fairroulette.round'); - evt.encode(wasmtypes.uint32ToString(number)); - evt.emit(); - } - - start(): void { - const evt = new wasmlib.EventEncoder('fairroulette.start'); - evt.emit(); - } - - stop(): void { - const evt = new wasmlib.EventEncoder('fairroulette.stop'); - evt.emit(); - } - - winner( - // the winning number - number: u16, - ): void { - const evt = new wasmlib.EventEncoder('fairroulette.winner'); - evt.encode(wasmtypes.uint16ToString(number)); - evt.emit(); - } -} -``` - - - - -Notice how the generated functions use the WasmLib EventEncoder to encode the parameters -into a single string before emitting it. Here is the way in which `fairroulette` emits the -`bet` event in its smart contract code: - - - - - -```go - f.Events.Bet(bet.Better.Address(), bet.Amount, bet.Number) -``` - - - - -```rust - f.events.bet(&bet.better.address(), bet.amount, bet.number); -``` - - - - -```ts -f.events.bet(bet.better.address(), bet.amount, bet.number); -``` - - - - -The smart contract client code can define handler functions to listen in to the event -stream and respond to any events it deems noteworthy. The [Schema Tool](usage.mdx) will -automatically generate the necessary client side code that properly listens for the -events, parses the event strings into a type-safe structure, and passes this structure to -the corresponding handler function. diff --git a/docs/content/developer/iota-evm/schema/how-tos/funcdesc.mdx b/docs/content/developer/iota-evm/schema/how-tos/funcdesc.mdx deleted file mode 100644 index d0f54f6bd56..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/funcdesc.mdx +++ /dev/null @@ -1,397 +0,0 @@ ---- -description: The schema tool provides us with an easy way to get access to smart contract functions through function descriptors, which allow you to initiate the function by calling it synchronously, or posting a request to run it asynchronously. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Add Function Descriptors - -You can use the [Schema Tool](usage.mdx) to access, and initiate smart contract functions seamlessly using -function descriptors. -These descriptors allow you to access optional [Params](params.mdx) and [Results](results.mdx) maps through strict -compile-time checked interfaces. - -## Function Descriptors Overview - -Function descriptors are structures that: - -- Offer access to the optional [Params](params.mdx) and [Results](results.mdx) maps. -- Allow synchronous or asynchronous initiation of functions through [`call()`](call.mdx) or [`post()`](post.mdx) requests. - -## The Schema Tool in Action - -The Schema Tool performs the following tasks: - -### 1. **Generate Specific Descriptors**: - -- For each function (`func`) and view. -- The outcome: Structs granting access to [Params](params.mdx) or [Results](results.mdx) maps, wherever specified. - -## 2. **Create the `ScFuncs` Interface**: - -- Facilitate the creation and initialization of each function descriptor. -- Incorporate a member function for every func or view to craft their respective function descriptor properly. - - - - - -```go -package dividend - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib" - -type DivideCall struct { - Func *wasmlib.ScFunc -} - -type InitCall struct { - Func *wasmlib.ScInitFunc - Params MutableInitParams -} - -type MemberCall struct { - Func *wasmlib.ScFunc - Params MutableMemberParams -} - -type SetOwnerCall struct { - Func *wasmlib.ScFunc - Params MutableSetOwnerParams -} - -type GetFactorCall struct { - Func *wasmlib.ScView - Params MutableGetFactorParams - Results ImmutableGetFactorResults -} - -type GetOwnerCall struct { - Func *wasmlib.ScView - Results ImmutableGetOwnerResults -} - -type Funcs struct{} - -var ScFuncs Funcs - -// divide tokens over members -func (sc Funcs) Divide(ctx wasmlib.ScFuncCallContext) *DivideCall { - return &DivideCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncDivide)} -} - -func (sc Funcs) Init(ctx wasmlib.ScFuncCallContext) *InitCall { - f := &InitCall{Func: wasmlib.NewScInitFunc(ctx, HScName, HFuncInit)} - f.Params.proxy = wasmlib.NewCallParamsProxy(&f.Func.ScView) - return f -} - -func (sc Funcs) Member(ctx wasmlib.ScFuncCallContext) *MemberCall { - f := &MemberCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncMember)} - f.Params.proxy = wasmlib.NewCallParamsProxy(&f.Func.ScView) - return f -} - -func (sc Funcs) SetOwner(ctx wasmlib.ScFuncCallContext) *SetOwnerCall { - f := &SetOwnerCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncSetOwner)} - f.Params.proxy = wasmlib.NewCallParamsProxy(&f.Func.ScView) - return f -} - -func (sc Funcs) GetFactor(ctx wasmlib.ScViewCallContext) *GetFactorCall { - f := &GetFactorCall{Func: wasmlib.NewScView(ctx, HScName, HViewGetFactor)} - f.Params.proxy = wasmlib.NewCallParamsProxy(f.Func) - wasmlib.NewCallResultsProxy(f.Func, &f.Results.proxy) - return f -} - -func (sc Funcs) GetOwner(ctx wasmlib.ScViewCallContext) *GetOwnerCall { - f := &GetOwnerCall{Func: wasmlib.NewScView(ctx, HScName, HViewGetOwner)} - wasmlib.NewCallResultsProxy(f.Func, &f.Results.proxy) - return f -} -``` - - - - - -```rust -use wasmlib::*; -use crate::*; - -pub struct DivideCall { - pub func: ScFunc, -} - -pub struct InitCall { - pub func: ScInitFunc, - pub params: MutableInitParams, -} - -pub struct MemberCall { - pub func: ScFunc, - pub params: MutableMemberParams, -} - -pub struct SetOwnerCall { - pub func: ScFunc, - pub params: MutableSetOwnerParams, -} - -pub struct GetFactorCall { - pub func: ScView, - pub params: MutableGetFactorParams, - pub results: ImmutableGetFactorResults, -} - -pub struct GetOwnerCall { - pub func: ScView, - pub results: ImmutableGetOwnerResults, -} - -pub struct ScFuncs { -} - -impl ScFuncs { - // divide tokens over members - pub fn divide(_ctx: &dyn ScFuncCallContext) -> DivideCall { - DivideCall { - func: ScFunc::new(HSC_NAME, HFUNC_DIVIDE), - } - } - - pub fn init(_ctx: &dyn ScFuncCallContext) -> InitCall { - let mut f = InitCall { - func: ScInitFunc::new(HSC_NAME, HFUNC_INIT), - params: MutableInitParams { proxy: Proxy::nil() }, - }; - ScInitFunc::link_params(&mut f.params.proxy, &f.func); - f - } - - pub fn member(_ctx: &dyn ScFuncCallContext) -> MemberCall { - let mut f = MemberCall { - func: ScFunc::new(HSC_NAME, HFUNC_MEMBER), - params: MutableMemberParams { proxy: Proxy::nil() }, - }; - ScFunc::link_params(&mut f.params.proxy, &f.func); - f - } - - pub fn set_owner(_ctx: &dyn ScFuncCallContext) -> SetOwnerCall { - let mut f = SetOwnerCall { - func: ScFunc::new(HSC_NAME, HFUNC_SET_OWNER), - params: MutableSetOwnerParams { proxy: Proxy::nil() }, - }; - ScFunc::link_params(&mut f.params.proxy, &f.func); - f - } - - pub fn get_factor(_ctx: &dyn ScViewCallContext) -> GetFactorCall { - let mut f = GetFactorCall { - func: ScView::new(HSC_NAME, HVIEW_GET_FACTOR), - params: MutableGetFactorParams { proxy: Proxy::nil() }, - results: ImmutableGetFactorResults { proxy: Proxy::nil() }, - }; - ScView::link_params(&mut f.params.proxy, &f.func); - ScView::link_results(&mut f.results.proxy, &f.func); - f - } - - pub fn get_owner(_ctx: &dyn ScViewCallContext) -> GetOwnerCall { - let mut f = GetOwnerCall { - func: ScView::new(HSC_NAME, HVIEW_GET_OWNER), - results: ImmutableGetOwnerResults { proxy: Proxy::nil() }, - }; - ScView::link_results(&mut f.results.proxy, &f.func); - f - } -} -``` - - - - -```ts -import * as wasmlib from 'wasmlib'; -import * as sc from './index'; - -export class DivideCall { - func: wasmlib.ScFunc; - public constructor(ctx: wasmlib.ScFuncCallContext) { - this.func = new wasmlib.ScFunc(ctx, sc.HScName, sc.HFuncDivide); - } -} - -export class DivideContext { - state: sc.MutableDividendState = new sc.MutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class InitCall { - func: wasmlib.ScInitFunc; - params: sc.MutableInitParams = new sc.MutableInitParams( - wasmlib.ScView.nilProxy, - ); - public constructor(ctx: wasmlib.ScFuncCallContext) { - this.func = new wasmlib.ScInitFunc(ctx, sc.HScName, sc.HFuncInit); - } -} - -export class InitContext { - params: sc.ImmutableInitParams = new sc.ImmutableInitParams( - wasmlib.paramsProxy(), - ); - state: sc.MutableDividendState = new sc.MutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class MemberCall { - func: wasmlib.ScFunc; - params: sc.MutableMemberParams = new sc.MutableMemberParams( - wasmlib.ScView.nilProxy, - ); - public constructor(ctx: wasmlib.ScFuncCallContext) { - this.func = new wasmlib.ScFunc(ctx, sc.HScName, sc.HFuncMember); - } -} - -export class MemberContext { - params: sc.ImmutableMemberParams = new sc.ImmutableMemberParams( - wasmlib.paramsProxy(), - ); - state: sc.MutableDividendState = new sc.MutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class SetOwnerCall { - func: wasmlib.ScFunc; - params: sc.MutableSetOwnerParams = new sc.MutableSetOwnerParams( - wasmlib.ScView.nilProxy, - ); - public constructor(ctx: wasmlib.ScFuncCallContext) { - this.func = new wasmlib.ScFunc(ctx, sc.HScName, sc.HFuncSetOwner); - } -} - -export class SetOwnerContext { - params: sc.ImmutableSetOwnerParams = new sc.ImmutableSetOwnerParams( - wasmlib.paramsProxy(), - ); - state: sc.MutableDividendState = new sc.MutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class GetFactorCall { - func: wasmlib.ScView; - params: sc.MutableGetFactorParams = new sc.MutableGetFactorParams( - wasmlib.ScView.nilProxy, - ); - results: sc.ImmutableGetFactorResults = new sc.ImmutableGetFactorResults( - wasmlib.ScView.nilProxy, - ); - public constructor(ctx: wasmlib.ScViewCallContext) { - this.func = new wasmlib.ScView(ctx, sc.HScName, sc.HViewGetFactor); - } -} - -export class GetFactorContext { - params: sc.ImmutableGetFactorParams = new sc.ImmutableGetFactorParams( - wasmlib.paramsProxy(), - ); - results: sc.MutableGetFactorResults = new sc.MutableGetFactorResults( - wasmlib.ScView.nilProxy, - ); - state: sc.ImmutableDividendState = new sc.ImmutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class GetOwnerCall { - func: wasmlib.ScView; - results: sc.ImmutableGetOwnerResults = new sc.ImmutableGetOwnerResults( - wasmlib.ScView.nilProxy, - ); - public constructor(ctx: wasmlib.ScViewCallContext) { - this.func = new wasmlib.ScView(ctx, sc.HScName, sc.HViewGetOwner); - } -} - -export class GetOwnerContext { - results: sc.MutableGetOwnerResults = new sc.MutableGetOwnerResults( - wasmlib.ScView.nilProxy, - ); - state: sc.ImmutableDividendState = new sc.ImmutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -export class ScFuncs { - // divide tokens over members - static divide(ctx: wasmlib.ScFuncCallContext): DivideCall { - return new DivideCall(ctx); - } - - static init(ctx: wasmlib.ScFuncCallContext): InitCall { - const f = new InitCall(ctx); - f.params = new sc.MutableInitParams(wasmlib.newCallParamsProxy(f.func)); - return f; - } - - static member(ctx: wasmlib.ScFuncCallContext): MemberCall { - const f = new MemberCall(ctx); - f.params = new sc.MutableMemberParams(wasmlib.newCallParamsProxy(f.func)); - return f; - } - - static setOwner(ctx: wasmlib.ScFuncCallContext): SetOwnerCall { - const f = new SetOwnerCall(ctx); - f.params = new sc.MutableSetOwnerParams(wasmlib.newCallParamsProxy(f.func)); - return f; - } - - static getFactor(ctx: wasmlib.ScViewCallContext): GetFactorCall { - const f = new GetFactorCall(ctx); - f.params = new sc.MutableGetFactorParams( - wasmlib.newCallParamsProxy(f.func), - ); - f.results = new sc.ImmutableGetFactorResults( - wasmlib.newCallResultsProxy(f.func), - ); - return f; - } - - static getOwner(ctx: wasmlib.ScViewCallContext): GetOwnerCall { - const f = new GetOwnerCall(ctx); - f.results = new sc.ImmutableGetOwnerResults( - wasmlib.newCallResultsProxy(f.func), - ); - return f; - } -} -``` - - - - -## `dividend` Example - Generated Code - -In the `dividend` example in `contract.xx`, specific structs for [Funcs and Views](../../schema/proxies.mdx) are created. -Here's how they operate: - -### Access - -- Via the `func` member within each struct. -- The `func` member type: Either `ScFunc` or `ScView`, contingent on whether it’s a function or a view. - -### Utilization - -- The `func` member is used to initiate function calls in various ways. diff --git a/docs/content/developer/iota-evm/schema/how-tos/funcs.mdx b/docs/content/developer/iota-evm/schema/how-tos/funcs.mdx deleted file mode 100644 index d7985a7af3f..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/funcs.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -description: The code generated for Funcs will be able to inspect and modify the smart contract state, whereas the code generated for Views will only be able to inspect the state. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Define Functions - -Here is the full schema definition file for the `dividend` example. We will now focus on -its [`funcs` and `views` sections](../../explanations/context.mdx). Since they are structured identically we will only need -to explain the layout of these sections once. - - - - -```yaml -name: Dividend -description: Simple dividend smart contract -structs: {} -typedefs: {} -state: - memberList: Address[] # array with all the recipients of this dividend - - # factors per member - - members: map[Address]Uint64 # map with all the recipient factors of this dividend - owner: AgentID # owner of contract, the only one who can call 'member' func - totalFactor: Uint64 # sum of all recipient factors -funcs: - # divide tokens over members - divide: {} - init: - params: - owner: AgentID? # optional owner of contract, defaults to contract creator - member: - access: owner # only defined owner of contract can add members - params: - address: Address # address of dividend recipient - factor: Uint64 # relative division factor - setOwner: - access: owner # only defined owner of contract can change owner - params: - owner: AgentID # new owner of smart contract -views: - getFactor: - params: - address: Address # address of dividend recipient - results: - factor: Uint64 # relative division factor - getOwner: - results: - owner: AgentID # current owner of this smart contract -``` - - - - - -As you can see each of the `funcs` and `views` sections defines their functions in the -same way. The only resulting difference is in the way the [Schema Tool](usage.mdx) -generates code for them: - -- The code generated for Funcs will be able to inspect and modify the smart contract state. -- The code generated for Views will only be able to inspect the state. - -Functions are defined as named subsections in the schema definition file. The name of the -subsection will become the name of the function. In turn, there can be 3 optional -subsections under each function subsection. - -- [`access`](access.mdx) indicates who is allowed to access the function. -- `params` holds the field definitions that describe the function parameters. -- `results` holds the field definitions that describe the function results. diff --git a/docs/content/developer/iota-evm/schema/how-tos/init.mdx b/docs/content/developer/iota-evm/schema/how-tos/init.mdx deleted file mode 100644 index 632674f5d92..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/init.mdx +++ /dev/null @@ -1,261 +0,0 @@ ---- -description: The init function will automatically be called immediately after the first time the contract has been deployed to the VM. This is a one-time initialization call, meant to be performed by the contract deployment mechanism. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Initialize a Smart Contract - -Smart contracts begin with a blank slate. You can define an initial state through the `init()` function. -This function is vital for setting configurations at the time of deployment. - -## Features of the `init()` Function - -### One-time Call - -Triggered automatically post-deployment, **it can only be used once** to set the initial configurations. - -### Security - -The ISC ensures the `init()` function is inaccessible after its initial execution, -safeguarding it from unauthorized accesses. - -### Necessity For a Separate Configuration Function - -To facilitate reconfiguration in the future, develop a distinct configuration function with proper access controls. - -## Usage Example - -To show how creating a smart contract with WasmLib works, we will slowly start fleshing -out the smart contract functions of the `dividend` example in this tutorial. Here is the -first part of the code that implements it, which contains the `init()` function: - - - - - -```go -// This example implements 'dividend', a simple smart contract that will -// automatically disperse iota tokens which are sent to the contract to a group -// of member accounts according to predefined division factors. The intent is -// to showcase basic functionality of WasmLib through a minimal implementation -// and not to come up with a complete robust real-world solution. -// Note that we have drawn sometimes out constructs that could have been done -// in a single line over multiple statements to be able to properly document -// step by step what is happening in the code. We also unnecessarily annotate -// all 'var' statements with their assignment type to improve understanding. - -//nolint:revive,goimports -package dividend - -import ( - "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib" - "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" -) - -// 'init' is used as a way to initialize a smart contract. It is an optional -// function that will automatically be called upon contract deployment. In this -// case we use it to initialize the 'owner' state variable so that we can later -// use this information to prevent non-owners from calling certain functions. -// The 'init' function takes a single optional parameter: -// - 'owner', which is the agent id of the entity owning the contract. -// When this parameter is omitted the owner will default to the contract creator. -func funcInit(ctx wasmlib.ScFuncContext, f *InitContext) { - // The schema tool has already created a proper InitContext for this function that - // allows us to access call parameters and state storage in a type-safe manner. - - // First we set up a default value for the owner in case the optional 'owner' - // parameter was omitted. We use the agent that sent the deploy request. - var owner wasmtypes.ScAgentID = ctx.RequestSender() - - // Now we check if the optional 'owner' parameter is present in the params map. - if f.Params.Owner().Exists() { - // Yes, it was present, so now we overwrite the default owner with - // the one specified by the 'owner' parameter. - owner = f.Params.Owner().Value() - } - - // Now that we have sorted out which agent will be the owner of this contract - // we will save this value in the 'owner' variable in state storage on the host. - // Read the documentation on schema.yaml to understand why this state variable is - // supported at compile-time by code generated from schema.yaml by the schema tool. - f.State.Owner().SetValue(owner) -} -``` - - - - -```rust -// This example implements 'dividend', a simple smart contract that will -// automatically disperse iota tokens which are sent to the contract to a group -// of member accounts according to predefined division factors. The intent is -// to showcase basic functionality of WasmLib through a minimal implementation -// and not to come up with a complete robust real-world solution. -// Note that we have drawn sometimes out constructs that could have been done -// in a single line over multiple statements to be able to properly document -// step by step what is happening in the code. We also unnecessarily annotate -// all 'let' statements with their assignment type to improve understanding. - -use wasmlib::*; - -use crate::*; - -// 'init' is used as a way to initialize a smart contract. It is an optional -// function that will automatically be called upon contract deployment. In this -// case we use it to initialize the 'owner' state variable so that we can later -// use this information to prevent non-owners from calling certain functions. -// The 'init' function takes a single optional parameter: -// - 'owner', which is the agent id of the entity owning the contract. -// When this parameter is omitted the owner will default to the contract creator. -pub fn func_init(ctx: &ScFuncContext, f: &InitContext) { - // The schema tool has already created a proper InitContext for this function that - // allows us to access call parameters and state storage in a type-safe manner. - - // First we set up a default value for the owner in case the optional 'owner' - // parameter was omitted. We use the agent that sent the deploy request. - let mut owner: ScAgentID = ctx.request_sender(); - - // Now we check if the optional 'owner' parameter is present in the params map. - if f.params.owner().exists() { - // Yes, it was present, so now we overwrite the default owner with - // the one specified by the 'owner' parameter. - owner = f.params.owner().value(); - } - - // Now that we have sorted out which agent will be the owner of this contract - // we will save this value in the 'owner' variable in state storage on the host. - // Read the documentation on schema.yaml to understand why this state variable is - // supported at compile-time by code generated from schema.yaml by the schema tool. - f.state.owner().set_value(&owner); -} -``` - - - - -```ts -// This example implements 'dividend', a simple smart contract that will -// automatically disperse iota tokens which are sent to the contract to a group -// of member accounts according to predefined division factors. The intent is -// to showcase basic functionality of WasmLib through a minimal implementation -// and not to come up with a complete robust real-world solution. -// Note that we have drawn sometimes out constructs that could have been done -// in a single line over multiple statements to be able to properly document -// step by step what is happening in the code. We also unnecessarily annotate -// all 'let' statements with their assignment type to improve understanding. - -import * as wasmlib from 'wasmlib'; -import * as sc from './index'; - -// 'init' is used as a way to initialize a smart contract. It is an optional -// function that will automatically be called upon contract deployment. In this -// case we use it to initialize the 'owner' state variable so that we can later -// use this information to prevent non-owners from calling certain functions. -// The 'init' function takes a single optional parameter: -// - 'owner', which is the agent id of the entity owning the contract. -// When this parameter is omitted the owner will default to the contract creator. -export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { - // The schema tool has already created a proper InitContext for this function that - // allows us to access call parameters and state storage in a type-safe manner. - - // First we set up a default value for the owner in case the optional 'owner' - // parameter was omitted. We use the agent that sent the deploy request. - let owner: wasmlib.ScAgentID = ctx.requestSender(); - - // Now we check if the optional 'owner' parameter is present in the params map. - if (f.params.owner().exists()) { - // Yes, it was present, so now we overwrite the default owner with - // the one specified by the 'owner' parameter. - owner = f.params.owner().value(); - } - - // Now that we have sorted out which agent will be the owner of this contract - // we will save this value in the 'owner' variable in state storage on the host. - // Read the documentation on schema.yaml to understand why this state variable is - // supported at compile-time by code generated from schema.yaml by the schema tool. - f.state.owner().setValue(owner); -} -``` - - - - -We define an owner variable and allow it to be something other than the default value of -the contract creator. It is always a good idea to be flexible enough to transfer -ownership to another entity if necessary. Remember that once a smart contract has been -deployed it is no longer possible to change it. Therefore, it is good practice to think -through situations that could require a change in advance and allow the contract itself to -handle such changes through its state by providing a proper function interface: - - - - - -```go -// 'setOwner' is used to change the owner of the smart contract. -// It updates the 'owner' state variable with the provided agent id. -// The 'setOwner' function takes a single mandatory parameter: -// - 'owner', which is the agent id of the entity that will own the contract. -// Only the current owner can change the owner. -func funcSetOwner(_ wasmlib.ScFuncContext, f *SetOwnerContext) { - // Note that the schema tool has already dealt with making sure that this function - // can only be called by the owner and that the required parameter is present. - // So once we get to this point in the code we can take that as a given. - - // Save the new owner parameter value in the 'owner' variable in state storage. - f.State.Owner().SetValue(f.Params.Owner().Value()) -} -``` - - - - -```rust -// 'setOwner' is used to change the owner of the smart contract. -// It updates the 'owner' state variable with the provided agent id. -// The 'setOwner' function takes a single mandatory parameter: -// - 'owner', which is the agent id of the entity that will own the contract. -// Only the current owner can change the owner. -pub fn func_set_owner(_ctx: &ScFuncContext, f: &SetOwnerContext) { - // Note that the schema tool has already dealt with making sure that this function - // can only be called by the owner and that the required parameter is present. - // So once we get to this point in the code we can take that as a given. - - // Save the new owner parameter value in the 'owner' variable in state storage. - f.state.owner().set_value(&f.params.owner().value()); -} -``` - - - - -```ts -// 'setOwner' is used to change the owner of the smart contract. -// It updates the 'owner' state variable with the provided agent id. -// The 'setOwner' function takes a single mandatory parameter: -// - 'owner', which is the agent id of the entity that will own the contract. -// Only the current owner can change the owner. -export function funcSetOwner( - ctx: wasmlib.ScFuncContext, - f: sc.SetOwnerContext, -): void { - // Note that the schema tool has already dealt with making sure that this function - // can only be called by the owner and that the required parameter is present. - // So once we get to this point in the code we can take that as a given. - - // Save the new owner parameter value in the 'owner' variable in state storage. - f.state.owner().setValue(f.params.owner().value()); -} -``` - - - - -Note that we only define a single owner here. -A proper fall-back could require multiple owners in case the owner entity could disappear, -which would allow others to take over instead of the contract becoming immutable about the “owner functionality”. -We cannot stress enough how important it is to **think through every aspect of a smart contract before deployment**. diff --git a/docs/content/developer/iota-evm/schema/how-tos/params.mdx b/docs/content/developer/iota-evm/schema/how-tos/params.mdx deleted file mode 100644 index 2deee9eb7f7..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/params.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -description: Learn how to define function parameters using the 'params' subsection, and how the Schema Tool facilitates this process. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Define Function Parameters - -You can use the optional [`params`](params.mdx) subsection to specify the parameters a function accepts. -This section can define both mandatory and optional parameters. -Optional parameters are denoted with a question mark `?` following the field type. - -## Schema Tool Automation - -The [Schema Tool](usage.mdx) streamlines the creation of functions by: - -- Generating an immutable structure holding parameter proxies from the [Params](params.mdx) map. -- Checking the presence and data type of non-optional parameters before the function call. - -These functionalities allow users to directly use parameter proxies in the structure passed to the function. - -:::note - -Omitting the `params` subsection results in no structure generation or parameter passing to the function. - -::: - -For example, here is the structure generated for the immutable [Params](params.mdx) for -the `member` function: - - - - - -```go -type ImmutableMemberParams struct { - proxy wasmtypes.Proxy -} - -// address of dividend recipient -func (s ImmutableMemberParams) Address() wasmtypes.ScImmutableAddress { - return wasmtypes.NewScImmutableAddress(s.proxy.Root(ParamAddress)) -} - -// relative division factor -func (s ImmutableMemberParams) Factor() wasmtypes.ScImmutableUint64 { - return wasmtypes.NewScImmutableUint64(s.proxy.Root(ParamFactor)) -} -``` - - - - -```rust -#[derive(Clone)] -pub struct ImmutableMemberParams { - pub(crate) proxy: Proxy, -} - -impl ImmutableMemberParams { - // address of dividend recipient - pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.proxy.root(PARAM_ADDRESS)) - } - - // relative division factor - pub fn factor(&self) -> ScImmutableUint64 { - ScImmutableUint64::new(self.proxy.root(PARAM_FACTOR)) - } -} -``` - - - - -```ts -export class ImmutableMemberParams extends wasmtypes.ScProxy { - // address of dividend recipient - address(): wasmtypes.ScImmutableAddress { - return new wasmtypes.ScImmutableAddress(this.proxy.root(sc.ParamAddress)); - } - - // relative division factor - factor(): wasmtypes.ScImmutableUint64 { - return new wasmtypes.ScImmutableUint64(this.proxy.root(sc.ParamFactor)); - } -} -``` - - - - -The [Schema Tool](usage.mdx) will also generate a mutable version of the structure, -suitable for providing the parameters when calling this smart contract function from any [Call Context](../../explanations/context.mdx). diff --git a/docs/content/developer/iota-evm/schema/how-tos/post.mdx b/docs/content/developer/iota-evm/schema/how-tos/post.mdx deleted file mode 100644 index c91219cfaec..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/post.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -description: Asynchronous function calls between smart contracts are posted as requests on the Tangle. They allow you to invoke any smart contract function that is not a View on any smart contract chain. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Post Asynchronous Requests - -Asynchronous requests enable interactions between different contracts. -Learn how to navigate the subtleties of posting asynchronous requests on the Tangle, -and how to effectively manage tokens and delays in execution. - -## Overview of Asynchronous Function Calls - -### Posting Requests on the Tangle - -Asynchronous function calls occur through requests posted on the Tangle. -These calls can invoke any function that is not a [View](views.mdx) on any smart contract chain. - -### Function Descriptor Methods - -To post a request, use the following methods from the function descriptor: - -- `post()`: Posts to the current chain. -- `postToChain()`: Allows specifying the chain through the chain ID parameter. - -### Delayed Execution - -You can set a delay in the execution using the `delay()` method, -enhancing smart contracts with timed actions or time-lock functionalities. - - - - - -```go -eor := ScFuncs.EndOfRound(ctx) -eor.Func.Delay(3600).Post() -``` - - - - -```rust -let eor = ScFuncs::end_of_round(ctx); -eor.func.delay(3600).post(); -``` - - - - -```ts -let eor = sc.ScFuncs.endOfRound(ctx); -eor.func.delay(3600).post(); -``` - - - - -## Managing Tokens in Asynchronous Calls - -### Mandatory Token Transfer - -Asynchronous requests require a minimum token transfer to cover the gas for function call execution. -Unused tokens return to the caller's account on the chain. - -### Specifying Tokens - -You can specify tokens: - -- Explicitly, as in to synchronous calls. -- Automatically, letting WasmLib determine the minimum requisite amount. - -### Prohibited `delay()` with [`call()`](call.mdx) - -Using `delay()` before a [`call()`](call.mdx) will cause a panic due to indeterminate user intentions at compile-time -without substantial overhead. - -## Handling Return Values - -If you need some return values, -you will have to create a mechanism that can do so. -For example, -you can provide a callback chain/contract/function combination as part of the input parameters of the requested function so that upon completion, -that function can asynchronously post the results to the indicated function. -It will require a certain degree of cooperation between both smart contracts. - -### Future Prospects - -Future developments aim to facilitate a generic mechanism for seamless return value communication in asynchronous function calls. diff --git a/docs/content/developer/iota-evm/schema/how-tos/results.mdx b/docs/content/developer/iota-evm/schema/how-tos/results.mdx deleted file mode 100644 index 053d8988a8b..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/results.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: Understand how to outline function results using the 'results' subsection and how the Schema Tool aids in this process. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Define Function Results - -You can use the optional `results` subsection to detail the results a function can produce. -This setup aligns with the field definitions seen in the [Params](params.mdx) subsection. - -## Schema Tool Features - -The [Schema Tool](usage.mdx) assists in this setup by: - -- Creating a mutable structure that includes proxies for each result variable found in the [Results](results.mdx) map. -- Enabling users to assign values to result variables via this generated structure during the function call. - -:::note - -If the `results` subsection is not used, no structure will be created or conveyed to the function. - -::: - -For example, here is the structure generated for the mutable results for the `getFactor` -function: - - - - - -```go -type MutableGetFactorResults struct { - proxy wasmtypes.Proxy -} - -// relative division factor -func (s MutableGetFactorResults) Factor() wasmtypes.ScMutableUint64 { - return wasmtypes.NewScMutableUint64(s.proxy.Root(ResultFactor)) -} -``` - - - - -```rust -#[derive(Clone)] -pub struct MutableGetFactorResults { - pub(crate) proxy: Proxy, -} - -impl MutableGetFactorResults { - // relative division factor - pub fn factor(&self) -> ScMutableUint64 { - ScMutableUint64::new(self.proxy.root(RESULT_FACTOR)) - } -} -``` - - - - -```ts -export class MutableGetFactorResults extends wasmtypes.ScProxy { - // relative division factor - factor(): wasmtypes.ScMutableUint64 { - return new wasmtypes.ScMutableUint64(this.proxy.root(sc.ResultFactor)); - } -} -``` - - - - -Note that the [Schema Tool](usage.mdx) will also generate an immutable version of the -structure, suitable for accessing the results after by the caller of this smart contract -function. diff --git a/docs/content/developer/iota-evm/schema/how-tos/spec.mdx b/docs/content/developer/iota-evm/schema/how-tos/spec.mdx deleted file mode 100644 index f48767e805e..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/spec.mdx +++ /dev/null @@ -1,460 +0,0 @@ ---- -description: The spec of schema tool and how to develop schema tool. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Spec - -## Workflow - -1. YAML file would be converted to a tree of `wasp_yaml.Node` -2. Convert the tree to a `model.SchemaDef` object -3. Compile the `model.SchemaDef` object to `model.Schema` object by calling `Compile()` -4. Convert `model.Schema` object to the Smart Contract of targeting languages - -## Types - -### model.SchemaDef - -`model.SchemaDef` is a intermediate object during the Smart Contract generation. An YAML file will be converted to `model.SchemaDef` object. -During the conversion, each YAML attribute except the top-level ones (`name`, `description`, `events`, `structs`, `typedefs`, `state`, `funcs`, `views`) will be converted into `DefElt`. - -Therefore, for YAML tags `name`, `description`, the values of them will be converted into 2 independent `DefElt`. - -``` -name: TestName -description: This is test description -``` - -For keywords that can have multiple values can be seen as either a one layer map (i.e., `typedefs` and `state`) -or two layer map (i.e., `typedefs` and `state`). A one layer map will be converted into `DefMap` which -is a map whose key and value are both `DefElt`. And a two layer map will be converted into `DefMapMap` which -is a map whose key is `DefElt` and value is `DefMap`. - -The definition of `DefElt` is shown as following, - -```go -type DefElt struct { - Val string - Comment string - Line int -} -``` - -It contains the raw value of the YAML attributes (without extracting the information), the comment belongs to the -YAML attribute, and the line number of the YAML attribute. - -Here is an example of one layer map - -```yaml -typedefs: - TestTypedef1: String - TestTypedef2: String -state: - TestState1: Int64[] - TestState2: Int64[] -``` - -And an example of two layer map - -```yaml -structs: - point: - x: Int32 - y: Int32 -funcs: - testFunc: - params: - testFuncParam: Uint64 - results: - testFuncResult: Uint64 -views: - testView: - params: - testViewParam: Uint64 - results: - testViewResult: Uint64 -``` - -Next, schema tool will set each fields in `SchemaDef` variable. - -```go -type SchemaDef struct { - Copyright string - Name DefElt - Description DefElt - Events DefMapMap - Structs DefMapMap - Typedefs DefMap - State DefMap - Funcs FuncDefMap - Views FuncDefMap -} -``` - -### model.Schema - -By calling `Schema.Compile()`, `model.SchemaDef` object will be compiled into `model.Schema`. -During the compilation, schema tool will extract the rules from the YAML attributes. - -Here is the definition of a `Schema` object. - -```go -type Schema struct { - ContractName string - Copyright string - PackageName string - Description string - CoreContracts bool - SchemaTime time.Time - Events []*Struct - Funcs []*Func - Params []*Field - Results []*Field - StateVars []*Field - Structs []*Struct - Typedefs []*Field -} -``` - -And let's take a close look at `Field` object. - -```go -type Field struct { - Name string // external name for this field - Alias string // internal name alias, can be different from Name - Array bool - FldComment string - MapKey string - Optional bool - Type string - BaseType bool - Comment string - Line int // the line number originally in yaml/json file -} -``` - -As you can see `typedefs` was a simple `DefMap`, which consists a map whose key and value are both `DefElt`, -and `DefElt` a simple object contains only raw string of the YAML attribute, comment and line number. -However, after the compilation, information is extracted from the raw string, so do some checks are conducted in this step. - -### Compile - -An emitter is used for filling corresponding values into templates under `tools/schema/generator`. -For how to do meta-programming with emitter, see section [Emitter](#emitter) - -```go -type ( - FieldMap map[string]*Field - FieldMapMap map[string]FieldMap - StringMap map[string]string - StringMapMap map[string]StringMap -) -``` - -## Comments - -### Header Comment and Line Comment - -Header comment has higher priority than the line comment. If there are both header comment and line comment presented at -same YAML attribute, then schema tool will keep only the header comment. - -### Comment Block - -A comment block is a chunk of comment that doesn't have a line break to separate it. Schema tool would take the -header comment that immediately followed by the YAML attribute or the line comment block if header comment block is not -presented. - -Therefore, for the following case - -```yaml -typedefs: - # header comment 1 - # header comment 2 - - # header comment 3 - # header comment 4 - TestTypedef: - String # line comment 1 - # line comment 2 -``` - -only these 2 lines - -```yaml -# header comment 3 -# header comment 4 -``` - -will be kept and presented in the final Smart Contract. - -And the next case - -```yaml -typedefs: - TestTypedef: - String # line comment 1 - # line comment 2 - - # line comment 3 - # line comment 4 -``` - -only these 2 lines - -```yaml -# line comment 1 -# line comment 2 -``` - -will be kept and presented in the final Smart Contract. - -## Emitter - -### Access Keys - -With `$` prepending a key (keys are set in `GenBase.setCommonKeys()`, `GenBase.setFieldKeys()`, `GenBase.setFuncKeys()`), schema tool can access the value of the key in `GenBase.keys` according to the current context. For example, if you want to access lower case package name, you can access it with `$package`. -To dynamically add a new key in templates (under gotemplates, rstemplates, and tstemplates), you can call `$#set` instruction, see section [set](#set) for more information. - -### Key And Plain String Concatenation - -To concatenate a value from accessing key and a plain string, you should use `$+` operator. -For example, here `FuncName` is a key that preserves the name of the function under current context, and we want to concatenate the function name with "Mutable" and "Results". -In other words, we want to do the same task as the following python code and get the result in `result` variable. - -```python -func_name = "..." # function name under current context -result = "Mutable" + func_name + "Results" # concatenate the strings into the result -``` - -In the schema template language, we should call `Mutable$FuncName$+Results`. - -### Instructions - -Keywords follows `$#` are the instructions defined in our schema template language. One thing you should aware, now, all the instruction should be presented at the beginning of each line. In other words, no spaces and characters are allowed to exist ahead of an instruction. -Here is the list of all the instruction keywords. - -- emit -- each -- func -- if -- set - -We are going to introduce how to use each instruction as follows. Or you can check the implementation of `GenBase.emit()` to know how are they implemented in detailed. - -#### emit - -`emit` is using for expanding templates. The syntax of `emit` instruction is - -``` -$#emit template -``` - -Here, `template` is any template which defined under gotemplates,rstemplates). -Templates are defined in `model.StringMap`. In the instruction call of `emit` just simply use the name of the template (the key in `model.StringMap`). -If you want to insert the `copyright` template to a assigned location, then you should call - -``` -$#emit copyright -``` - -#### each - -`each` processes the template for each item in the array. The syntax of `each` instruction is - -``` -$#each array template -``` - -Here `array` is either a predefined keyword (we are going to introduce each of them as follow) or a multi-lines string. -If a multi-lines string is presented, then the multi-lines string will be expanded and append newline escape character of targeting languages in the end of each line. - -##### event - -Iterate the fields in a event. - -##### events - -Iterate all the `events` in the contract. - -##### func - -Iterate all the `funcs` in the contract. - -##### mandatory - -Iterate all the mandatory fields in the current processed function. The mandatory field must be basetype and not an array or a map. - -##### param - -Iterate all the `params` fields in the current processed function. - -##### params - -Iterate all the `params` fields in the current contract. - -##### result - -Iterate all the `results` fields in the current processed function. - -##### results - -Iterate all the `results` fields in the current contract. - -##### state - -Iterate all the `state` in the contract. - -##### struct - -Iterate the fields in a struct. - -##### structs - -Iterate all the `structs` in the contract. - -##### typedef - -Iterate all the typedefs in the contract. - -#### func - -Currently not used. - -#### if - -The syntax of `if` is - -``` -$#if condition template [elseTemplate] -``` - -`if` processes template when the named condition is true, and it processes the optional `elseTemplate` when the named condition is false - -`condition` is either the predefined conditions (explained as following) or a key that may exist in `keys`. -If a key is presented, then `if` instruction would be used for check whether this key exists in `keys` or not. If the key exists, then `if` will return true, otherwise it will return false. - -And here are the predefined conditions. - -##### array - -Is the current processed field an array? - -##### basetype - -Is the current processed field in basetype? `basetype`s are defined in the map `FieldTypes` in `tools/schema/model/field.go`. - -##### core - -Is the current processed contract a core contract? - -##### event - -Does the current processed event have any field? - -##### events - -Is there any event in the current processed contract? - -##### exist - -Does the value of key `proxy` exist? - -##### func - -Is the current processed function a `func` or a `view`? Return true if it is a `func`. - -##### funcs - -Is there any function in the current processed contract? - -##### init - -Is the current function an init function? An init function will automatically be called immediately after the first time the contract has been deployed to the VM. - -##### mandatory - -Is current field a mandatory field? - -##### map - -Is current processed field a map (check if the `currentField.MapKey` is empty)? - -##### mut - -Is the value in key `mut` Mutable? - -##### param - -Does the current processed function have any parameter? - -##### params - -Does the current contract have any `params` field? - -##### ptrs - -Does the current processed function have either `params` or `results`. -This is used for implementing function object in Rust and TypeScript. - -##### result - -Does the current processed function have any return value? - -##### results - -Does the current contract have any `results` field? - -##### state - -Does the current contract have any `state` field? - -##### structs - -Does the current contract have any `structs` field? - -##### this - -Is the alias name of the current processed field `this`? - -##### typedef - -Is the current processed field a `typedef`? - -##### typedefs - -Does the current contract have any `typedefs` field? - -##### view - -Is the current processed function a `view` or a `func`? Return true if it is a `view`. - -##### else - -If you want to process a template under negate condition, then you can call - -``` -$#if condition else elseTemplate -``` - -Here `else` is a predefined empty template, which is defined at[`tools/schema/generator/templates.go`. - -#### set - -`set` is used for To dynamically specify a value to a certain key. The syntax is - -``` -$#set key value -``` - -For example, if you want to dynamically add a new key `initFunc` with the value in key `nil` you can call - -``` -$#set initFunc $nil -``` - -A special key `exist` is used to add a newly generated type. diff --git a/docs/content/developer/iota-evm/schema/how-tos/state.mdx b/docs/content/developer/iota-evm/schema/how-tos/state.mdx deleted file mode 100644 index cf74498d578..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/state.mdx +++ /dev/null @@ -1,179 +0,0 @@ ---- -description: The smart contract state storage on the host consists of a single key/value map, as long as you access the data in the same way that you used to store it, you will always get valid data back. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Define the State - -In smart contract state storage, there is only a single key/value map, -where both the key and value are constituted of raw data bytes. -To ensure the retrieval of valid data, access it in the manner it was stored. - -The [Schema Tool](usage.mdx) facilitates this by generating a type-safe layer -that oversees the consistent utilization of the expected data type during _data storage_ and retrieval. - -## State Section in Schema Definition File - -In the schema definition file the `state` section hosts field definitions, -delineating the variables stored in the state storage. -Each field is defined with a YAML key/value pair, indicating its name and data type. -This pair can optionally be followed by a descriptive comment. - -With these details, the [Schema Tool](usage.mdx) creates specific code to type-safely access state variables. -Take a closer look at the `state` section in the `dividend` example to understand this better: - - - - -```yaml -state: - memberList: Address[] # array with all the recipients of this dividend - - # factors per member - - members: map[Address]Uint64 # map with all the recipient factors of this dividend - owner: AgentID # owner of contract, the only one who can call 'member' func - totalFactor: Uint64 # sum of all recipient factors -``` - - - - -### Simple Variables - -Starting with straightforward state variables, `totalFactor`, -and `owner` are characterized as Uint64 and AgentID, respectively. -These represent predefined [WasmLib value types](../../../../references/iota-evm/wasm-lib-data-types.mdx). - -### Arrays and Maps - -Next, the `memberList` variable denoted by empty brackets `[]`, symbolizing an array. -This array accommodates elements of a homogeneous predefined Address value type. - -Lastly, the `members` variable, signified as a map with `map[]`, houses keys of a uniform predefined Address type. -Following the brackets, the homogeneous value type, here `Uint64`, is mentioned. - - - - - -```go -type MutableDividendState struct { - proxy wasmtypes.Proxy -} - -func (s MutableDividendState) AsImmutable() ImmutableDividendState { - return ImmutableDividendState(s) -} - -// array with all the recipients of this dividend -func (s MutableDividendState) MemberList() ArrayOfMutableAddress { - return ArrayOfMutableAddress{proxy: s.proxy.Root(StateMemberList)} -} - -// map with all the recipient factors of this dividend -func (s MutableDividendState) Members() MapAddressToMutableUint64 { - return MapAddressToMutableUint64{proxy: s.proxy.Root(StateMembers)} -} - -// owner of contract, the only one who can call 'member' func -func (s MutableDividendState) Owner() wasmtypes.ScMutableAgentID { - return wasmtypes.NewScMutableAgentID(s.proxy.Root(StateOwner)) -} - -// sum of all recipient factors -func (s MutableDividendState) TotalFactor() wasmtypes.ScMutableUint64 { - return wasmtypes.NewScMutableUint64(s.proxy.Root(StateTotalFactor)) -} -``` - - - - -```rust -#[derive(Clone)] -pub struct MutableDividendState { - pub(crate) proxy: Proxy, -} - -impl MutableDividendState { - pub fn as_immutable(&self) -> ImmutableDividendState { - ImmutableDividendState { proxy: self.proxy.root("") } - } - - // array with all the recipients of this dividend - pub fn member_list(&self) -> ArrayOfMutableAddress { - ArrayOfMutableAddress { proxy: self.proxy.root(STATE_MEMBER_LIST) } - } - - // map with all the recipient factors of this dividend - pub fn members(&self) -> MapAddressToMutableUint64 { - MapAddressToMutableUint64 { proxy: self.proxy.root(STATE_MEMBERS) } - } - - // owner of contract, the only one who can call 'member' func - pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.proxy.root(STATE_OWNER)) - } - - // sum of all recipient factors - pub fn total_factor(&self) -> ScMutableUint64 { - ScMutableUint64::new(self.proxy.root(STATE_TOTAL_FACTOR)) - } -} -``` - - - - -```ts -export class MutableDividendState extends wasmtypes.ScProxy { - asImmutable(): sc.ImmutableDividendState { - return new sc.ImmutableDividendState(this.proxy); - } - - // array with all the recipients of this dividend - memberList(): sc.ArrayOfMutableAddress { - return new sc.ArrayOfMutableAddress(this.proxy.root(sc.StateMemberList)); - } - - // map with all the recipient factors of this dividend - members(): sc.MapAddressToMutableUint64 { - return new sc.MapAddressToMutableUint64(this.proxy.root(sc.StateMembers)); - } - - // owner of contract, the only one who can call 'member' func - owner(): wasmtypes.ScMutableAgentID { - return new wasmtypes.ScMutableAgentID(this.proxy.root(sc.StateOwner)); - } - - // sum of all recipient factors - totalFactor(): wasmtypes.ScMutableUint64 { - return new wasmtypes.ScMutableUint64(this.proxy.root(sc.StateTotalFactor)); - } -} -``` - - - - -## Generated Code Overview - -Examining the `state.xx`code, generated by the [Schema Tool](usage.mdx), -you can find the `MutableDividendState` struct. -This interface allows type-safe access to each state variable through mutable [proxies](../../schema/proxies.mdx), -establishing a one-to-one relationship with the `state` section in the schema definition file. - -### Proxy Interfaces - -Note the generated proxy interface named `MutableDividendState` for mutable `dividend` state. -It enables type-safe proxy object access for each corresponding variable. -Moreover, the tool auto-generates intermediate map and array proxy types, -such as `ArrayOfMutableAddress` and `MapAddressToMutableUint64`, -enforcing the utilization of respective homogeneous types. - -See the full `state.xx` for more details. diff --git a/docs/content/developer/iota-evm/schema/how-tos/structs.mdx b/docs/content/developer/iota-evm/schema/how-tos/structs.mdx deleted file mode 100644 index 85170509aa6..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/structs.mdx +++ /dev/null @@ -1,506 +0,0 @@ ---- -description: You can use structs directly as a type in state storage definitions and the schema tool will automatically generate the proxy code to access it properly. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Structured Data Types - -The [Schema Tool](usage.mdx) allows you to define your structured data types that are -composed of the predefined WasmLib value data types. The tool will generate a struct with -named fields according to the definition in the schema definition file and also will -generate code to serialize and deserialize the structure to a byte array so that it can -be saved as a single unit of data bytes, for example in state storage. - -You can use structs directly as a type in state storage definitions, and the -[Schema Tool](usage.mdx) will automatically generate the proxy code to access and -(de)serialize it properly. - -For example, let's say you are creating a `betting` smart contract. Then, you would want to -store information for each bet. The Bet structure could consist of the bet amount and -time, the number of the item that was bet on, and the agent ID of the one who placed the -bet. And you would keep track of all bets in state storage in an array of Bet structs. To -do so, you would insert the following into the schema definition file: - - - - -```yaml -structs: - Bet: - amount: Int64 # bet amount - better: AgentID # who placed this bet - number: Int32 # number of item we bet on - time: Int64 # timestamp of this bet -state: - bets: Bet[] # all bets that were made in this round -``` - - - - -The [Schema Tool](usage.mdx) will generate the following code in `structs.xx` for the Bet -struct: - - - - - -```go -package betting - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" - -type Bet struct { - // bet amount - Amount int64 - // who placed this bet - Better wasmtypes.ScAgentID - // number of item we bet on - Number int32 - // timestamp of this bet - Time int64 -} - -func NewBetFromBytes(buf []byte) *Bet { - dec := wasmtypes.NewWasmDecoder(buf) - data := &Bet{} - data.Amount = wasmtypes.Int64Decode(dec) - data.Better = wasmtypes.AgentIDDecode(dec) - data.Number = wasmtypes.Int32Decode(dec) - data.Time = wasmtypes.Int64Decode(dec) - dec.Close() - return data -} - -func (o *Bet) Bytes() []byte { - enc := wasmtypes.NewWasmEncoder() - wasmtypes.Int64Encode(enc, o.Amount) - wasmtypes.AgentIDEncode(enc, o.Better) - wasmtypes.Int32Encode(enc, o.Number) - wasmtypes.Int64Encode(enc, o.Time) - return enc.Buf() -} - -type ImmutableBet struct { - proxy wasmtypes.Proxy -} - -func (o ImmutableBet) Exists() bool { - return o.proxy.Exists() -} - -func (o ImmutableBet) Value() *Bet { - return NewBetFromBytes(o.proxy.Get()) -} - -type MutableBet struct { - proxy wasmtypes.Proxy -} - -func (o MutableBet) Delete() { - o.proxy.Delete() -} - -func (o MutableBet) Exists() bool { - return o.proxy.Exists() -} - -func (o MutableBet) SetValue(value *Bet) { - o.proxy.Set(value.Bytes()) -} - -func (o MutableBet) Value() *Bet { - return NewBetFromBytes(o.proxy.Get()) -} -``` - - - - -```rust -use wasmlib::*; - -#[derive(Clone)] -pub struct Bet { - // bet amount - pub amount : i64, - // who placed this bet - pub better : ScAgentID, - // number of item we bet on - pub number : i32, - // timestamp of this bet - pub time : i64, -} - -impl Bet { - pub fn from_bytes(bytes: &[u8]) -> Bet { - let mut dec = WasmDecoder::new(bytes); - Bet { - amount : int64_decode(&mut dec), - better : agent_id_decode(&mut dec), - number : int32_decode(&mut dec), - time : int64_decode(&mut dec), - } - } - - pub fn to_bytes(&self) -> Vec { - let mut enc = WasmEncoder::new(); - int64_encode(&mut enc, self.amount); - agent_id_encode(&mut enc, &self.better); - int32_encode(&mut enc, self.number); - int64_encode(&mut enc, self.time); - enc.buf() - } -} - -#[derive(Clone)] -pub struct ImmutableBet { - pub(crate) proxy: Proxy, -} - -impl ImmutableBet { - pub fn exists(&self) -> bool { - self.proxy.exists() - } - - pub fn value(&self) -> Bet { - Bet::from_bytes(&self.proxy.get()) - } -} - -#[derive(Clone)] -pub struct MutableBet { - pub(crate) proxy: Proxy, -} - -impl MutableBet { - pub fn delete(&self) { - self.proxy.delete(); - } - - pub fn exists(&self) -> bool { - self.proxy.exists() - } - - pub fn set_value(&self, value: &Bet) { - self.proxy.set(&value.to_bytes()); - } - - pub fn value(&self) -> Bet { - Bet::from_bytes(&self.proxy.get()) - } -} -``` - - - - -```ts -import * as wasmtypes from 'wasmlib/wasmtypes'; - -export class Bet { - // bet amount - amount: i64 = 0; - // who placed this bet - better: wasmtypes.ScAgentID = wasmtypes.agentIDFromBytes([]); - // number of item we bet on - number: i32 = 0; - // timestamp of this bet - time: i64 = 0; - - static fromBytes(buf: u8[]): Bet { - const dec = new wasmtypes.WasmDecoder(buf); - const data = new Bet(); - data.amount = wasmtypes.int64Decode(dec); - data.better = wasmtypes.agentIDDecode(dec); - data.number = wasmtypes.int32Decode(dec); - data.time = wasmtypes.int64Decode(dec); - dec.close(); - return data; - } - - bytes(): u8[] { - const enc = new wasmtypes.WasmEncoder(); - wasmtypes.int64Encode(enc, this.amount); - wasmtypes.agentIDEncode(enc, this.better); - wasmtypes.int32Encode(enc, this.number); - wasmtypes.int64Encode(enc, this.time); - return enc.buf(); - } -} - -export class ImmutableBet extends wasmtypes.ScProxy { - exists(): bool { - return this.proxy.exists(); - } - - value(): Bet { - return Bet.fromBytes(this.proxy.get()); - } -} - -export class MutableBet extends wasmtypes.ScProxy { - delete(): void { - this.proxy.delete(); - } - - exists(): bool { - return this.proxy.exists(); - } - - setValue(value: Bet): void { - this.proxy.set(value.bytes()); - } - - value(): Bet { - return Bet.fromBytes(this.proxy.get()); - } -} -``` - - - - -Notice how the generated ImmutableBet and MutableBet proxies use the fromBytes() and -toBytes() (de)serialization code to automatically transform byte arrays into Bet structs. - -The generated code in `state.xx` that implements the state interface is shown here: - - - - - -```go -package betting - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" - -type ArrayOfImmutableBet struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfImmutableBet) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfImmutableBet) GetBet(index uint32) ImmutableBet { - return ImmutableBet{proxy: a.proxy.Index(index)} -} - -type ImmutableBettingState struct { - proxy wasmtypes.Proxy -} - -// all bets that were made in this round -func (s ImmutableBettingState) Bets() ArrayOfImmutableBet { - return ArrayOfImmutableBet{proxy: s.proxy.Root(StateBets)} -} - -// current owner of this smart contract -func (s ImmutableBettingState) Owner() wasmtypes.ScImmutableAgentID { - return wasmtypes.NewScImmutableAgentID(s.proxy.Root(StateOwner)) -} - -type ArrayOfMutableBet struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfMutableBet) AppendBet() MutableBet { - return MutableBet{proxy: a.proxy.Append()} -} - -func (a ArrayOfMutableBet) Clear() { - a.proxy.ClearArray() -} - -func (a ArrayOfMutableBet) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfMutableBet) GetBet(index uint32) MutableBet { - return MutableBet{proxy: a.proxy.Index(index)} -} - -type MutableBettingState struct { - proxy wasmtypes.Proxy -} - -func (s MutableBettingState) AsImmutable() ImmutableBettingState { - return ImmutableBettingState(s) -} - -// all bets that were made in this round -func (s MutableBettingState) Bets() ArrayOfMutableBet { - return ArrayOfMutableBet{proxy: s.proxy.Root(StateBets)} -} - -// current owner of this smart contract -func (s MutableBettingState) Owner() wasmtypes.ScMutableAgentID { - return wasmtypes.NewScMutableAgentID(s.proxy.Root(StateOwner)) -} -``` - - - - -```rust -use wasmlib::*; - -use crate::*; - -#[derive(Clone)] -pub struct ArrayOfImmutableBet { - pub(crate) proxy: Proxy, -} - -impl ArrayOfImmutableBet { - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_bet(&self, index: u32) -> ImmutableBet { - ImmutableBet { proxy: self.proxy.index(index) } - } -} - -#[derive(Clone)] -pub struct ImmutableBettingState { - pub(crate) proxy: Proxy, -} - -impl ImmutableBettingState { - // all bets that were made in this round - pub fn bets(&self) -> ArrayOfImmutableBet { - ArrayOfImmutableBet { proxy: self.proxy.root(STATE_BETS) } - } - - // current owner of this smart contract - pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.proxy.root(STATE_OWNER)) - } -} - -#[derive(Clone)] -pub struct ArrayOfMutableBet { - pub(crate) proxy: Proxy, -} - -impl ArrayOfMutableBet { - - pub fn append_bet(&self) -> MutableBet { - MutableBet { proxy: self.proxy.append() } - } - pub fn clear(&self) { - self.proxy.clear_array(); - } - - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_bet(&self, index: u32) -> MutableBet { - MutableBet { proxy: self.proxy.index(index) } - } -} - -#[derive(Clone)] -pub struct MutableBettingState { - pub(crate) proxy: Proxy, -} - -impl MutableBettingState { - pub fn as_immutable(&self) -> ImmutableBettingState { - ImmutableBettingState { proxy: self.proxy.root("") } - } - - // all bets that were made in this round - pub fn bets(&self) -> ArrayOfMutableBet { - ArrayOfMutableBet { proxy: self.proxy.root(STATE_BETS) } - } - - // current owner of this smart contract - pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.proxy.root(STATE_OWNER)) - } -} -``` - - - - -```ts -import * as wasmtypes from 'wasmlib/wasmtypes'; -import * as sc from './index'; - -export class ArrayOfImmutableBet extends wasmtypes.ScProxy { - length(): u32 { - return this.proxy.length(); - } - - getBet(index: u32): sc.ImmutableBet { - return new sc.ImmutableBet(this.proxy.index(index)); - } -} - -export class ImmutableBettingState extends wasmtypes.ScProxy { - // all bets that were made in this round - bets(): sc.ArrayOfImmutableBet { - return new sc.ArrayOfImmutableBet(this.proxy.root(sc.StateBets)); - } - - // current owner of this smart contract - owner(): wasmtypes.ScImmutableAgentID { - return new wasmtypes.ScImmutableAgentID(this.proxy.root(sc.StateOwner)); - } -} - -export class ArrayOfMutableBet extends wasmtypes.ScProxy { - appendBet(): sc.MutableBet { - return new sc.MutableBet(this.proxy.append()); - } - - clear(): void { - this.proxy.clearArray(); - } - - length(): u32 { - return this.proxy.length(); - } - - getBet(index: u32): sc.MutableBet { - return new sc.MutableBet(this.proxy.index(index)); - } -} - -export class MutableBettingState extends wasmtypes.ScProxy { - asImmutable(): sc.ImmutableBettingState { - return new sc.ImmutableBettingState(this.proxy); - } - - // all bets that were made in this round - bets(): sc.ArrayOfMutableBet { - return new sc.ArrayOfMutableBet(this.proxy.root(sc.StateBets)); - } - - // current owner of this smart contract - owner(): wasmtypes.ScMutableAgentID { - return new wasmtypes.ScMutableAgentID(this.proxy.root(sc.StateOwner)); - } -} -``` - - - - -The results are ImmutableBettingState and MutableBettingState structures that can -directly interface to the state of the betting contract. - -Note how the comments from the schema definition file have been copied to the code's structure definition and access -functions for the fields. This will allow your development environment to pop up context - sensitive help -for those fields and access functions. diff --git a/docs/content/developer/iota-evm/schema/how-tos/thunks.mdx b/docs/content/developer/iota-evm/schema/how-tos/thunks.mdx deleted file mode 100644 index 9a71ccb32f5..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/thunks.mdx +++ /dev/null @@ -1,372 +0,0 @@ ---- -description: Learn about thunk functions and how the Schema Tool uses them to facilitate smart contract function calls. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# Thunk Functions - -In software development, thunk functions are a crucial part of setting up smart contracts correctly, -facilitating type-safety and easy mapping of function names to their actual implementations. - -## Overview - -A thunk is a wrapper function employed to inject code before and/or after a function call. -It helps in adapting functions to meet changing demands. -Through the [Schema Tool](usage.mdx), -thunks are generated to establish correct calls to smart contract functions and to ensure type-safety. -They share a common function signature, fostering a straightforward creation of a function table for generic calls. - -## Role in Wasm - -Working hand in hand with the WebAssembly (Wasm) format, -thunks play a pivotal role in the `lib.xx` component. -Below is how a chunk of a `dividend` smart contract would look (the detailed thunk function content is omitted): - - - - - -```go -var exportMap = wasmlib.ScExportMap{ - Names: []string{ - FuncDivide, - FuncInit, - FuncMember, - FuncSetOwner, - ViewGetFactor, - ViewGetOwner, - }, - Funcs: []wasmlib.ScFuncContextFunction{ - funcDivideThunk, - funcInitThunk, - funcMemberThunk, - funcSetOwnerThunk, - }, - Views: []wasmlib.ScViewContextFunction{ - viewGetFactorThunk, - viewGetOwnerThunk, - }, -} - -func OnDispatch(index int32) { - exportMap.Dispatch(index) -} - -func funcDivideThunk(ctx wasmlib.ScFuncContext) {} -func funcInitThunk(ctx wasmlib.ScFuncContext) {} -func funcMemberThunk(ctx wasmlib.ScFuncContext) {} -func funcSetOwnerThunk(ctx wasmlib.ScFuncContext) {} -func viewGetFactorThunk(ctx wasmlib.ScViewContext) {} -func viewGetOwnerThunk(ctx wasmlib.ScViewContext) {} -``` - - - - -```rust -const EXPORT_MAP: ScExportMap = ScExportMap { - names: &[ - FUNC_DIVIDE, - FUNC_INIT, - FUNC_MEMBER, - FUNC_SET_OWNER, - VIEW_GET_FACTOR, - VIEW_GET_OWNER, - ], - funcs: &[ - func_divide_thunk, - func_init_thunk, - func_member_thunk, - func_set_owner_thunk, - ], - views: &[ - view_get_factor_thunk, - view_get_owner_thunk, - ], -}; - -pub fn on_dispatch(index: i32) { - EXPORT_MAP.dispatch(index); -} - -fn func_divide_thunk(ctx: &ScFuncContext) {} -fn func_init_thunk(ctx: &ScFuncContext) {} -fn func_member_thunk(ctx: &ScFuncContext) {} -fn func_set_owner_thunk(ctx: &ScFuncContext) {} -fn view_get_factor_thunk(ctx: &ScViewContext) {} -fn view_get_owner_thunk(ctx: &ScViewContext) {} -``` - - - - -```ts -const exportMap: wasmlib.ScExportMap = { - names: [ - sc.FuncDivide, - sc.FuncInit, - sc.FuncMember, - sc.FuncSetOwner, - sc.ViewGetFactor, - sc.ViewGetOwner, - ], - funcs: [funcDivideThunk, funcInitThunk, funcMemberThunk, funcSetOwnerThunk], - views: [viewGetFactorThunk, viewGetOwnerThunk], -}; - -export function on_dispatch(index: i32): void { - exportMap.dispatch(index); -} - -function funcDivideThunk(ctx: ScFuncContext) {} -function funcInitThunk(ctx: ScFuncContext) {} -function funcMemberThunk(ctx: ScFuncContext) {} -function funcSetOwnerThunk(ctx: ScFuncContext) {} -function viewGetFactorThunk(ctx: ScViewContext) {} -function viewGetOwnerThunk(ctx: ScViewContext) {} -``` - - - - -## The Dispatch Process - -The central player here is the `OnDispatch()` function, -called by the primary Wasm file, essentially a dynamic link library. -This mechanism keeps the smart contract (SC) code self-contained and versatile, -fitting for both Wasm requirements and direct client-side executions. - -To meet Wasm's demands, we implement `on_load()` and `on_call()` callbacks that collaborate with `OnDispatch()` in the `lib.xx`, -orchestrating a seamless dispatch process. - -### `on_load()` - -The `on_load()` Wasm function will be called by the Wasm VM host upon loading of the Wasm -code. It will inform the host of all the function ids and types (Func or View) that this -smart contract provides. - -### `on_call()` - -When the host needs to call a function of the smart contract it will call the `on_call()` -callback function with the corresponding function id, and then the `on_call()` function -will dispatch the call via the `ScExportMap` mapping table that was generated by the -[Schema Tool](usage.mdx) to the proper associated thunk function. - -## Generated Main Entry Point - -Here is the generated `main.xx` that forms the main entry point for the Wasm code: - - - - - -```go -//go:build wasm -// +build wasm - -package main - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmvmhost/go/wasmvmhost" - -import "github.com/iotaledger/wasp/contracts/wasm/dividend/go/dividend" - -func main() { -} - -func init() { - wasmvmhost.ConnectWasmHost() -} - -//export on_call -func onCall(index int32) { - dividend.OnDispatch(index) -} - -//export on_load -func onLoad() { - dividend.OnDispatch(-1) -} -``` - - - - -```rust -use dividend::*; -use wasmvmhost::*; - -#[no_mangle] -fn on_call(index: i32) { - WasmVmHost::connect(); - on_dispatch(index); -} - -#[no_mangle] -fn on_load() { - WasmVmHost::connect(); - on_dispatch(-1); -} -``` - - - - -```ts -import * as wasmvmhost from 'wasmvmhost'; -import * as sc from './dividend'; - -export function on_call(index: i32): void { - wasmvmhost.WasmVMHost.connect(); - sc.onDispatch(index); -} - -export function on_load(): void { - wasmvmhost.WasmVMHost.connect(); - sc.onDispatch(-1); -} -``` - - - - -Finally, here is an example implementation of a thunk function for the `setOwner()` -contract function. You can examine the other thunk functions that all follow the same -pattern in the generated `lib.xx`: - - - - - -```go -type SetOwnerContext struct { - Params ImmutableSetOwnerParams - State MutableDividendState -} - -func funcSetOwnerThunk(ctx wasmlib.ScFuncContext) { - ctx.Log("dividend.funcSetOwner") - f := &SetOwnerContext{ - Params: ImmutableSetOwnerParams{ - proxy: wasmlib.NewParamsProxy(), - }, - State: MutableDividendState{ - proxy: wasmlib.NewStateProxy(), - }, - } - - // only defined owner of contract can change owner - access := f.State.Owner() - ctx.Require(access.Exists(), "access not set: owner") - ctx.Require(ctx.Caller() == access.Value(), "no permission") - - ctx.Require(f.Params.Owner().Exists(), "missing mandatory owner") - funcSetOwner(ctx, f) - ctx.Log("dividend.funcSetOwner ok") -} -``` - - - - -```rust -pub struct SetOwnerContext { - params: ImmutableSetOwnerParams, - state: MutableDividendState, -} - -fn func_set_owner_thunk(ctx: &ScFuncContext) { - ctx.log("dividend.funcSetOwner"); - let f = SetOwnerContext { - params: ImmutableSetOwnerParams { proxy: params_proxy() }, - state: MutableDividendState { proxy: state_proxy() }, - }; - - // only defined owner of contract can change owner - let access = f.state.owner(); - ctx.require(access.exists(), "access not set: owner"); - ctx.require(ctx.caller() == access.value(), "no permission"); - - ctx.require(f.params.owner().exists(), "missing mandatory owner"); - func_set_owner(ctx, &f); - ctx.log("dividend.funcSetOwner ok"); -} -``` - - - - -```ts -// this class is actually defined in contract.ts -export class SetOwnerContext { - params: sc.ImmutableSetOwnerParams = new sc.ImmutableSetOwnerParams( - wasmlib.paramsProxy(), - ); - state: sc.MutableDividendState = new sc.MutableDividendState( - wasmlib.ScState.proxy(), - ); -} - -function funcSetOwnerThunk(ctx: wasmlib.ScFuncContext): void { - ctx.log('dividend.funcSetOwner'); - let f = new sc.SetOwnerContext(); - - // only defined owner of contract can change owner - const access = f.state.owner(); - ctx.require(access.exists(), 'access not set: owner'); - ctx.require(ctx.caller().equals(access.value()), 'no permission'); - - ctx.require(f.params.owner().exists(), 'missing mandatory owner'); - sc.funcSetOwner(ctx, f); - ctx.log('dividend.funcSetOwner ok'); -} -``` - - - - -## The Thunk Function in Action - -### Log the Contract and Function Name - -First, the thunk logs the contract and function name to show that the call has started. - -### Set Up Strongly Typed Context - -Then it sets up a strongly typed function-specific context structure. First, we add the -function-specific immutable [Params](params.mdx) interface structure, which is only -present when the function can have parameters. Then we add the contract-specific -[State](state.mdx) interface structure. In this case, it is mutable because setOwner is a -[Func](funcs.mdx). For [Views](views.mdx), this would be an immutable state interface. -Finally, we would add the function-specific mutable [Results](results.mdx) interface -structure, which is only present when the function returns results. -This is not the case for this `setOwner()` function. - -### Set Up Access Control - -Next, it sets up access control for the function according to the schema definition file. -In this case, it retrieves the `owner` state variable through the function context, -requires that the variable exists, and then requires that the function's `caller()` -equals that value. Any failing requirement will panic out of the thunk function with an -error message. So, this code ensures that only the contract owner can call this -function. - -### Verify Function Parameters - -Now we get to the point where we can use the function-specific [Params](params.mdx) -interface to check for mandatory parameters. Each mandatory parameter is required to -exist, or else we will panic out of the thunk function with an error message. - -### Call the Smart Contract Function - -With the setup and automated checks completed, we now call the actual smart contract -function implementation the user maintains. -After this function has been completed, -we would process the returned results for those functions that have any (in this case, we -don't have results). -Finally, we log that the contract function has been completed -successfully. Remember that any error within the user function will cause a panic, so this -logging will never occur in case that happens. diff --git a/docs/content/developer/iota-evm/schema/how-tos/transfers.mdx b/docs/content/developer/iota-evm/schema/how-tos/transfers.mdx deleted file mode 100644 index 00253975dee..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/transfers.mdx +++ /dev/null @@ -1,238 +0,0 @@ ---- -description: Explore methods in the Call Context that facilitate the handling and transfer of asset balances in smart contracts using the `dividend` example. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - - -# Transfer Tokens - -The [Call Context](../../explanations/context.mdx) in smart contracts introduces two vital methods - -`balances()` and `allowance()` - to manage token balances efficiently. -Understanding how to use these can be foundational in setting up smart contracts, -as demonstrated in the `dividend` smart contract example. - -## Methods Overview - -### 1. **`balances()` Method**: - -- **Functionality**: Fetch the present asset balances regulated by the smart contract. -- **Access**: Through the `ScBalances` proxy. - -### 2. **`allowance()` Method**: - -- **Functionality**: Verify the caller assets that the current smart contract function can use. -- **Transfer Requirement**: Assets are not automatically transferred; - the function must instigate the transfer explicitly within the allowed limit. -- **Access**: Through the `ScBalances` proxy. - -### 3. **`transfer_allowed()` Method**: - -- **Functionality**: Facilitate asset transfers from the caller's on-chain account to another on-chain account. -- **Proxy**: uses the `ScTransfer` proxy, a mutable variant of `ScBalances`. -- **Application**: In the `dividend` contract, it aids in distributing iotas to member accounts. - -## Dividend Smart Contract Example - -The `dividend` smart contract operates on a principle of equitable asset distribution to its members based on predefined factors. -Here's how it works: - -1. **Setup**: Establish a list of members with associated address/factor pairs and calculate the total factor sum. -2. **Function**: The `divide()` function manages the dividend distribution. -3. **Dividend Allocation**: - - **Input**: IOTAs sent to the `divide()` function. - - **Distribution**: Proportional to the individual's factor. - - **Example**: For factors A:50, B:30, and C:20 (total 100): - - **A receives**: 50/100 of the input iotas. - - **B receives**: 30/100 of the input iotas. - - **C receives**: 20/100 of the input iotas. - -In this system, asset distribution is transparent, fair, and automated, ensuring a streamlined division process. - -Here is the `divide` function: - - - - - -```go -// 'divide' is a function that will take any iotas it receives and properly -// disperse them to the accounts in the member list according to the dispersion -// factors associated with these accounts. -// Anyone can send iotas to this function and they will automatically be -// divided over the member list. Note that this function does not deal with -// fractions. It simply truncates the calculated amount to the nearest lower -// integer and keeps any remaining tokens in the sender account. -func funcDivide(ctx wasmlib.ScFuncContext, f *DivideContext) { - // Create an ScBalances proxy to the allowance balances for this - // smart contract. - var allowance *wasmlib.ScBalances = ctx.Allowance() - - // Retrieve the amount of plain iota tokens from the account balance. - var amount uint64 = allowance.BaseTokens() - - // Retrieve the pre-calculated totalFactor value from the state storage. - var totalFactor uint64 = f.State.TotalFactor().Value() - - // Get the proxy to the 'members' map in the state storage. - var members MapAddressToMutableUint64 = f.State.Members() - - // Get the proxy to the 'memberList' array in the state storage. - var memberList ArrayOfMutableAddress = f.State.MemberList() - - // Determine the current length of the memberList array. - var size uint32 = memberList.Length() - - // Loop through all indexes of the memberList array. - for i := uint32(0); i < size; i++ { - // Retrieve the next indexed address from the memberList array. - var address wasmtypes.ScAddress = memberList.GetAddress(i).Value() - - // Retrieve the factor associated with the address from the members map. - var factor uint64 = members.GetUint64(address).Value() - - // Calculate the fair share of tokens to disperse to this member based on the - // factor we just retrieved. Note that the result will been truncated. - var share uint64 = amount * factor / totalFactor - - // Is there anything to disperse to this member? - if share > 0 { - // Yes, so let's set up an ScTransfer map proxy that transfers the - // calculated amount of tokens. - var transfer *wasmlib.ScTransfer = wasmlib.NewScTransferBaseTokens(share) - - // Perform the actual transfer of tokens from the caller allowance - // to the member account. - ctx.TransferAllowed(address.AsAgentID(), transfer) - } - } -} -``` - - - - -```rust -// 'divide' is a function that will take any iotas it receives and properly -// disperse them to the accounts in the member list according to the dispersion -// factors associated with these accounts. -// Anyone can send iotas to this function and they will automatically be -// divided over the member list. Note that this function does not deal with -// fractions. It simply truncates the calculated amount to the nearest lower -// integer and keeps any remaining tokens in its own account. They will be added -// to any next round of tokens received prior to calculation of the new -// dividend amounts. -pub fn func_divide(ctx: &ScFuncContext, f: &DivideContext) { - - // Create an ScBalances proxy to the allowance balances for this - // smart contract. - let allowance: ScBalances = ctx.allowance(); - - // Retrieve the amount of plain iota tokens from the account balance. - let amount: u64 = allowance.base_tokens(); - - // Retrieve the pre-calculated totalFactor value from the state storage. - let total_factor: u64 = f.state.total_factor().value(); - - // Get the proxy to the 'members' map in the state storage. - let members: MapAddressToMutableUint64 = f.state.members(); - - // Get the proxy to the 'memberList' array in the state storage. - let member_list: ArrayOfMutableAddress = f.state.member_list(); - - // Determine the current length of the memberList array. - let size: u32 = member_list.length(); - - // Loop through all indexes of the memberList array. - for i in 0..size { - // Retrieve the next indexed address from the memberList array. - let address: ScAddress = member_list.get_address(i).value(); - - // Retrieve the factor associated with the address from the members map. - let factor: u64 = members.get_uint64(&address).value(); - - // Calculate the fair share of tokens to disperse to this member based on the - // factor we just retrieved. Note that the result will be truncated. - let share: u64 = amount * factor / total_factor; - - // Is there anything to disperse to this member? - if share > 0 { - // Yes, so let's set up an ScTransfer map proxy that transfers the - // calculated amount of tokens. - let transfers: ScTransfer = ScTransfer::base_tokens(share); - - // Perform the actual transfer of tokens from the caller allowance - // to the member account. - ctx.transfer_allowed(&address.as_agent_id(), &transfers, true); - } - } -} -``` - - - - -```ts -// 'divide' is a function that will take any iotas it receives and properly -// disperse them to the accounts in the member list according to the dispersion -// factors associated with these accounts. -// Anyone can send iotas to this function and they will automatically be -// divided over the member list. Note that this function does not deal with -// fractions. It simply truncates the calculated amount to the nearest lower -// integer and keeps any remaining tokens in its own account. They will be added -// to any next round of tokens received prior to calculation of the new -// dividend amounts. -export function funcDivide( - ctx: wasmlib.ScFuncContext, - f: sc.DivideContext, -): void { - // Create an ScBalances proxy to the allowance balances for this - // smart contract. - let allowance: wasmlib.ScBalances = ctx.allowance(); - - // Retrieve the allowed amount of plain iota tokens from the account balance. - let amount: u64 = allowance.baseTokens(); - - // Retrieve the pre-calculated totalFactor value from the state storage. - let totalFactor: u64 = f.state.totalFactor().value(); - - // Get the proxy to the 'members' map in the state storage. - let members: sc.MapAddressToMutableUint64 = f.state.members(); - - // Get the proxy to the 'memberList' array in the state storage. - let memberList: sc.ArrayOfMutableAddress = f.state.memberList(); - - // Determine the current length of the memberList array. - let size: u32 = memberList.length(); - - // Loop through all indexes of the memberList array. - for (let i: u32 = 0; i < size; i++) { - // Retrieve the next indexed address from the memberList array. - let address: wasmlib.ScAddress = memberList.getAddress(i).value(); - - // Retrieve the factor associated with the address from the members map. - let factor: u64 = members.getUint64(address).value(); - - // Calculate the fair share of tokens to disperse to this member based on the - // factor we just retrieved. Note that the result will be truncated. - let share: u64 = (amount * factor) / totalFactor; - - // Is there anything to disperse to this member? - if (share > 0) { - // Yes, so let's set up an ScTransfer proxy that transfers the - // calculated amount of tokens. - let transfers: wasmlib.ScTransfer = wasmlib.ScTransfer.baseTokens(share); - - // Perform the actual transfer of tokens from the caller allowance - // to the member account. - ctx.transferAllowed(address.asAgentID(), transfers); - } - } -} -``` - - - diff --git a/docs/content/developer/iota-evm/schema/how-tos/typedefs.mdx b/docs/content/developer/iota-evm/schema/how-tos/typedefs.mdx deleted file mode 100644 index bab014bb9a6..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/typedefs.mdx +++ /dev/null @@ -1,449 +0,0 @@ ---- -description: You can add a typedefs section to the schema definition file, where you can define a single type name for a container type. This way you can easily create containers that contain container types. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - - -# Type Definitions - -:::note WasmLib Types - -You can find the complete list of [WasmLib Data Types](../../../../references/iota-evm/wasm-lib-data-types.mdx) in the reference section. - -::: - -The WasmLib allows of [container types](../../schema/proxies.mdx#container-proxies), but it -is not possible to specify these types directly because the type name syntax only allows -you to specify a single container type. - -There is a simple solution to this problem. You can add a `typedefs` section to the schema -definition file, where you can define a single type name for a container type. That way -you can easily create containers that contain such container types. The -[Schema Tool](usage.mdx) will automatically generate the in-between proxy types necessary -to make this work. - -To keep it at the `betting` smart contract from [the previous section](structs.mdx), -imagine you want to keep track of all betting rounds. Since a betting round contains an -array of all bets in a round, if it weren't for typedefs you could not define it easily. - -Instead, now you add the following to your schema definition file: - - - - -```yaml -typedefs: - BettingRound: Bet[] // one round of bets -state: - rounds: BettingRound[] // keep track of all betting rounds -``` - - - - -The [Schema Tool](usage.mdx) will generate the following code in `typedefs.xx` for the -`BettingRound` type: - - - - - -```go -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" - -type ArrayOfImmutableBet struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfImmutableBet) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfImmutableBet) GetBet(index uint32) ImmutableBet { - return ImmutableBet{proxy: a.proxy.Index(index)} -} - -type ImmutableBettingRound = ArrayOfImmutableBet - -type ArrayOfMutableBet struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfMutableBet) AppendBet() MutableBet { - return MutableBet{proxy: a.proxy.Append()} -} - -func (a ArrayOfMutableBet) Clear() { - a.proxy.ClearArray() -} - -func (a ArrayOfMutableBet) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfMutableBet) GetBet(index uint32) MutableBet { - return MutableBet{proxy: a.proxy.Index(index)} -} - -type MutableBettingRound = ArrayOfMutableBet -``` - - - - -```rust -use wasmlib::*; -use crate::*; - -#[derive(Clone)] -pub struct ArrayOfImmutableBet { - pub(crate) proxy: Proxy, -} - -impl ArrayOfImmutableBet { - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_bet(&self, index: u32) -> ImmutableBet { - ImmutableBet { proxy: self.proxy.index(index) } - } -} - -pub type ImmutableBettingRound = ArrayOfImmutableBet; - -#[derive(Clone)] -pub struct ArrayOfMutableBet { - pub(crate) proxy: Proxy, -} - -impl ArrayOfMutableBet { - - pub fn append_bet(&self) -> MutableBet { - MutableBet { proxy: self.proxy.append() } - } - pub fn clear(&self) { - self.proxy.clear_array(); - } - - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_bet(&self, index: u32) -> MutableBet { - MutableBet { proxy: self.proxy.index(index) } - } -} - -pub type MutableBettingRound = ArrayOfMutableBet; -``` - - - - -```ts -import * as wasmtypes from 'wasmlib/wasmtypes'; -import * as sc from './index'; - -export class ArrayOfImmutableBet extends wasmtypes.ScProxy { - length(): u32 { - return this.proxy.length(); - } - - getBet(index: u32): sc.ImmutableBet { - return new sc.ImmutableBet(this.proxy.index(index)); - } -} - -export class ImmutableBettingRound extends ArrayOfImmutableBet {} - -export class ArrayOfMutableBet extends wasmtypes.ScProxy { - appendBet(): sc.MutableBet { - return new sc.MutableBet(this.proxy.append()); - } - - clear(): void { - this.proxy.clearArray(); - } - - length(): u32 { - return this.proxy.length(); - } - - getBet(index: u32): sc.MutableBet { - return new sc.MutableBet(this.proxy.index(index)); - } -} - -export class MutableBettingRound extends ArrayOfMutableBet {} -``` - - - - -Note how `ImmutableBettingRound` and `MutableBettingRound` type aliases are created -for the types `ArrayOfImmutableBet` and `ArrayOfMutableBet`. These are subsequently used -in the state definition that is generated in `state.xx`: - - - - - -```go -package betting - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes" - -type ArrayOfImmutableBettingRound struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfImmutableBettingRound) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfImmutableBettingRound) GetBettingRound(index uint32) ImmutableBettingRound { - return ImmutableBettingRound{proxy: a.proxy.Index(index)} -} - -type ImmutableBettingState struct { - proxy wasmtypes.Proxy -} - -// all bets that were made in this round -func (s ImmutableBettingState) Bets() ArrayOfImmutableBet { - return ArrayOfImmutableBet{proxy: s.proxy.Root(StateBets)} -} - -// current owner of this smart contract -func (s ImmutableBettingState) Owner() wasmtypes.ScImmutableAgentID { - return wasmtypes.NewScImmutableAgentID(s.proxy.Root(StateOwner)) -} - -func (s ImmutableBettingState) Rounds() ArrayOfImmutableBettingRound { - return ArrayOfImmutableBettingRound{proxy: s.proxy.Root(StateRounds)} -} - -type ArrayOfMutableBettingRound struct { - proxy wasmtypes.Proxy -} - -func (a ArrayOfMutableBettingRound) AppendBettingRound() MutableBettingRound { - return MutableBettingRound{proxy: a.proxy.Append()} -} - -func (a ArrayOfMutableBettingRound) Clear() { - a.proxy.ClearArray() -} - -func (a ArrayOfMutableBettingRound) Length() uint32 { - return a.proxy.Length() -} - -func (a ArrayOfMutableBettingRound) GetBettingRound(index uint32) MutableBettingRound { - return MutableBettingRound{proxy: a.proxy.Index(index)} -} - -type MutableBettingState struct { - proxy wasmtypes.Proxy -} - -func (s MutableBettingState) AsImmutable() ImmutableBettingState { - return ImmutableBettingState(s) -} - -// all bets that were made in this round -func (s MutableBettingState) Bets() ArrayOfMutableBet { - return ArrayOfMutableBet{proxy: s.proxy.Root(StateBets)} -} - -// current owner of this smart contract -func (s MutableBettingState) Owner() wasmtypes.ScMutableAgentID { - return wasmtypes.NewScMutableAgentID(s.proxy.Root(StateOwner)) -} - -func (s MutableBettingState) Rounds() ArrayOfMutableBettingRound { - return ArrayOfMutableBettingRound{proxy: s.proxy.Root(StateRounds)} -} -``` - - - - -```rust -use wasmlib::*; - -use crate::*; - -#[derive(Clone)] -pub struct ArrayOfImmutableBettingRound { - pub(crate) proxy: Proxy, -} - -impl ArrayOfImmutableBettingRound { - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_betting_round(&self, index: u32) -> ImmutableBettingRound { - ImmutableBettingRound { proxy: self.proxy.index(index) } - } -} - -#[derive(Clone)] -pub struct ImmutableBettingState { - pub(crate) proxy: Proxy, -} - -impl ImmutableBettingState { - // all bets that were made in this round - pub fn bets(&self) -> ArrayOfImmutableBet { - ArrayOfImmutableBet { proxy: self.proxy.root(STATE_BETS) } - } - - // current owner of this smart contract - pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.proxy.root(STATE_OWNER)) - } - - pub fn rounds(&self) -> ArrayOfImmutableBettingRound { - ArrayOfImmutableBettingRound { proxy: self.proxy.root(STATE_ROUNDS) } - } -} - -#[derive(Clone)] -pub struct ArrayOfMutableBettingRound { - pub(crate) proxy: Proxy, -} - -impl ArrayOfMutableBettingRound { - - pub fn append_betting_round(&self) -> MutableBettingRound { - MutableBettingRound { proxy: self.proxy.append() } - } - pub fn clear(&self) { - self.proxy.clear_array(); - } - - pub fn length(&self) -> u32 { - self.proxy.length() - } - - - pub fn get_betting_round(&self, index: u32) -> MutableBettingRound { - MutableBettingRound { proxy: self.proxy.index(index) } - } -} - -#[derive(Clone)] -pub struct MutableBettingState { - pub(crate) proxy: Proxy, -} - -impl MutableBettingState { - pub fn as_immutable(&self) -> ImmutableBettingState { - ImmutableBettingState { proxy: self.proxy.root("") } - } - - // all bets that were made in this round - pub fn bets(&self) -> ArrayOfMutableBet { - ArrayOfMutableBet { proxy: self.proxy.root(STATE_BETS) } - } - - // current owner of this smart contract - pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.proxy.root(STATE_OWNER)) - } - - pub fn rounds(&self) -> ArrayOfMutableBettingRound { - ArrayOfMutableBettingRound { proxy: self.proxy.root(STATE_ROUNDS) } - } -} -``` - - - - -```ts -import * as wasmtypes from 'wasmlib/wasmtypes'; -import * as sc from './index'; - -export class ArrayOfImmutableBettingRound extends wasmtypes.ScProxy { - length(): u32 { - return this.proxy.length(); - } - - getBettingRound(index: u32): sc.ImmutableBettingRound { - return new sc.ImmutableBettingRound(this.proxy.index(index)); - } -} - -export class ImmutableBettingState extends wasmtypes.ScProxy { - // all bets that were made in this round - bets(): sc.ArrayOfImmutableBet { - return new sc.ArrayOfImmutableBet(this.proxy.root(sc.StateBets)); - } - - // current owner of this smart contract - owner(): wasmtypes.ScImmutableAgentID { - return new wasmtypes.ScImmutableAgentID(this.proxy.root(sc.StateOwner)); - } - - rounds(): sc.ArrayOfImmutableBettingRound { - return new sc.ArrayOfImmutableBettingRound(this.proxy.root(sc.StateRounds)); - } -} - -export class ArrayOfMutableBettingRound extends wasmtypes.ScProxy { - appendBettingRound(): sc.MutableBettingRound { - return new sc.MutableBettingRound(this.proxy.append()); - } - - clear(): void { - this.proxy.clearArray(); - } - - length(): u32 { - return this.proxy.length(); - } - - getBettingRound(index: u32): sc.MutableBettingRound { - return new sc.MutableBettingRound(this.proxy.index(index)); - } -} - -export class MutableBettingState extends wasmtypes.ScProxy { - asImmutable(): sc.ImmutableBettingState { - return new sc.ImmutableBettingState(this.proxy); - } - - // all bets that were made in this round - bets(): sc.ArrayOfMutableBet { - return new sc.ArrayOfMutableBet(this.proxy.root(sc.StateBets)); - } - - // current owner of this smart contract - owner(): wasmtypes.ScMutableAgentID { - return new wasmtypes.ScMutableAgentID(this.proxy.root(sc.StateOwner)); - } - - rounds(): sc.ArrayOfMutableBettingRound { - return new sc.ArrayOfMutableBettingRound(this.proxy.root(sc.StateRounds)); - } -} -``` - - - - -Notice how the `rounds()` member function returns a proxy to an array of `BettingRound`. -Which in turn is an array of `Bet`. So, the desired result has been achieved. And every -access step only allows you to take the path laid out, which is checked at -compile-time. diff --git a/docs/content/developer/iota-evm/schema/how-tos/usage.mdx b/docs/content/developer/iota-evm/schema/how-tos/usage.mdx deleted file mode 100644 index d1cc2aafc7a..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/usage.mdx +++ /dev/null @@ -1,294 +0,0 @@ ---- -description: The `schema` tool will assist in creating a smart contract as unobtrusively as possible. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - - -# Use the Schema Tool - -Creating smart contracts is simplified using the _Schema Tool_. -This guide outlines the initial steps to set up a new smart contract from scratch. - -## Step 1: Establish a Central Folder - -Select a central folder to house all your smart contracts. -You'll create a separate subfolder for each contract within this central repository. - -Next, choose a descriptive, capitalized camel case name for your contract, like `MySmartContract`. - -## Step 2: Create a Subfolder - -After naming your smart contract, you should create a corresponding subfolder. -Open your terminal, navigate to the central folder, -and initialize your project with the Schema Tool using the following command: - -```shell -schema -init MySmartContract -``` - -This command will create a subfolder named `mysmartcontract` and generate an initial YAML -schema definition file inside this subfolder. Note that the generated subfolder name is -all lower-case. This is to conform to best practices for package names in most languages. -The generated schema definition file looks like this: - - - - -```yaml -name: MySmartContract -description: MySmartContract description -events: {} -structs: {} -typedefs: {} -state: - owner: AgentID // current owner of this smart contract -funcs: - init: - params: - owner: AgentID? // optional owner of this smart contract - setOwner: - access: owner // current owner of this smart contract - params: - owner: AgentID // new owner of this smart contract -views: - getOwner: - results: - owner: AgentID // current owner of this smart contract -``` - - - - -After initializing your project with the Schema Tool, -a pre-populated schema definition file will be generated in the `mysmartcontract` subfolder. -This file contains the necessary sections and functions to manage your smart contract's ownership. - -## Naming Conventions - -Ensure to follow the _camel case_ naming convention in the schema definition file. Here is how to use it: - -- Function and variable names: start with a lowercase letter (e.g., `myFunction`) -- Type names: start with an uppercase letter (e.g., `MyType`) - -## Customizing Fields - -Begin by updating the `description` field with a relevant description of your smart contract. -It is the perfect time to add any known definitions to the necessary sections. - -## Generating Initial Code - -Navigate to your `mysmartcontract` subfolder to generate the initial code for your preferred programming language using -the Schema Tool. - - - - - -If you want to generate Go code, you should run the schema tool with the `-go` option like -this: - -```shell -schema -go -``` - - - - -If you want to generate Rust code, you should run the schema tool with the `-rs` option -like this: - -```shell -schema -rs -``` - - - - -If you want to generate TypeScript code, you should run the schema tool with the `-ts` -option like this: - -```shell -schema -ts -``` - - - - -If you want to generate more than one language your can simply specify multiple options. -For example, to generate both Rust and Go code you would specify both options like this: - -```shell -schema -rs -go -``` - -The schema tool will generate a complete set of source files for the desired language(s), -that will compile successfully into a Wasm code file. You compile these as follows: - - - - - -```shell -tinygo build -target wasm go/main.go -``` - -This will use the Go source files in the go/mysmartcontract subfolder. The only file in -this folder that you should edit manually is `mysmartcontract.go`. All other source files -will be regenerated and overwritten whenever the schema tool is run again. - -See the [TinyGo](https://tinygo.org/) documentation for more build options. - - - - -After generating the Rust code, you should first modify the Cargo.toml file to your -liking, and potentially add the new project to a Rust workspace. Cargo.toml will not be -regenerated once it already exists. Then build the code as follows: - -```shell -wasm-pack build -``` - -This will use Rust source files in the `src` subfolder. The only file in this folder that -you should edit manually is `mysmartcontract.rs`. All other source files will be -regenerated and overwritten whenever the schema tool is run again. - -See the [wasm-pack](https://rustwasm.github.io/wasm-pack/) documentation for more build -options. - - - - -```shell -npx asc ts/mysmartcontract/lib.ts --outFile mysmartcontract_ts.wasm --lib path/to/node_modules -``` - -This will use the TypeScript source files in the ts/mysmartcontract subfolder. The only -file in this folder that you should edit manually is `mysmartcontract.ts`. All other -source files will be regenerated and overwritten whenever the schema tool is run again. - -See the [AssemblyScript](https://www.assemblyscript.org/) documentation for more build -options. - - - - -The generated code is essentially identical for each language, barring some language -idiosyncrasy differences. Just view different language files with the same name next to, -each other, and you will see what we mean. - -Here is an example of the initially generated code, `mysmartcontract.xx` looks like this -before you even start modifying it: - - - - - -```go -package mysmartcontract - -import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib" - - -func funcInit(ctx wasmlib.ScFuncContext, f *InitContext) { - if f.Params.Owner().Exists() { - f.State.Owner().SetValue(f.Params.Owner().Value()) - return - } - f.State.Owner().SetValue(ctx.RequestSender()) -} - -func funcSetOwner(ctx wasmlib.ScFuncContext, f *SetOwnerContext) { - f.State.Owner().SetValue(f.Params.Owner().Value()) -} - -func viewGetOwner(ctx wasmlib.ScViewContext, f *GetOwnerContext) { - f.Results.Owner().SetValue(f.State.Owner().Value()) -} -``` - - - - -```rust -use wasmlib::*; - -use crate::*; - -pub fn func_init(ctx: &ScFuncContext, f: &InitContext) { - if f.params.owner().exists() { - f.state.owner().set_value(&f.params.owner().value()); - return; - } - f.state.owner().set_value(&ctx.request_sender()); -} - -pub fn func_set_owner(_ctx: &ScFuncContext, f: &SetOwnerContext) { - f.state.owner().set_value(&f.params.owner().value()); -} - -pub fn view_get_owner(_ctx: &ScViewContext, f: &GetOwnerContext) { - f.results.owner().set_value(&f.state.owner().value()); -} -``` - - - - -```ts -import * as wasmlib from 'wasmlib'; -import * as wasmtypes from 'wasmlib/wasmtypes'; -import * as sc from './index'; - -export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { - if (f.params.owner().exists()) { - f.state.owner().setValue(f.params.owner().value()); - return; - } - f.state.owner().setValue(ctx.requestSender()); -} - -export function funcSetOwner( - ctx: wasmlib.ScFuncContext, - f: sc.SetOwnerContext, -): void { - f.state.owner().setValue(f.params.owner().value()); -} - -export function viewGetOwner( - ctx: wasmlib.ScViewContext, - f: sc.GetOwnerContext, -): void { - f.results.owner().setValue(f.state.owner().value()); -} -``` - - - - -The schema tool automatically generates an initial working version of the functions to maintain the smart contract owner, -catering to most use cases. - -To streamline the building process, configure a build rule in your environment. -This rule should trigger the schema tool with the necessary parameters -whenever there are changes in the schema definition file. -This setup ensures automatic file regeneration, -eliminating the need to run the schema tool manually after each modification. -The tool regenerates code only if it detects alterations since its last operation. -To override this and force regeneration, include the `-force` flag in the command line parameter. - -### Creating Smart Contracts using AssemblyScript - - diff --git a/docs/content/developer/iota-evm/schema/how-tos/views.mdx b/docs/content/developer/iota-evm/schema/how-tos/views.mdx deleted file mode 100644 index f70a6ec1a32..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/views.mdx +++ /dev/null @@ -1,128 +0,0 @@ ---- -description: Explore the characteristics and constraints of view-only functions in smart contracts, illustrated through a 'getFactor' function example. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - - -# Use View-Only Functions - -View-only functions, also known as "views", -are smart contract functions specialized in retrieving state information without altering the smart contract's state. - -## Characteristics - -### Immutable Proxies - -All state storage accesses occur through immutable proxies, ensuring the state remains unchanged. - -### Restricted Functionalities - -Views have a limited [Call Context](../../explanations/context.mdx), -disabling any function that might induce state changes, including token transactions. - -### Intra-chain Calls - -While they can [`call()`](call.mdx) other views within the same chain, -they cannot initiate non-view functions or [`post()`](post.mdx) cross-chain requests. - -### Return Data - -These functions are designed to return data to the caller, as they can't induce any other external effects. - -## Use Case: 'getFactor' Function - -To illustrate the use of view-only functions, consider the `getFactor()` function integrated in the `dividend` smart contract: - - - - - -```go - -// 'getFactor' is a simple View function. It will retrieve the factor -// associated with the (mandatory) address parameter it was provided with. -func viewGetFactor(_ wasmlib.ScViewContext, f *GetFactorContext) { - // Since we are sure that the 'address' parameter actually exists we can - // retrieve its actual value into an ScAddress value type. - var address wasmtypes.ScAddress = f.Params.Address().Value() - - // Create an ScImmutableMap proxy to the 'members' map in the state storage. - // Note that for views this is an *immutable* map as opposed to the *mutable* - // map we can access from the *mutable* state that gets passed to funcs. - var members MapAddressToImmutableUint64 = f.State.Members() - - // Retrieve the factor associated with the address parameter. - var factor uint64 = members.GetUint64(address).Value() - - // Set the factor in the results map of the function context. - // The contents of this results map is returned to the caller of the function. - f.Results.Factor().SetValue(factor) -} -``` - - - - -```rust -// 'getFactor' is a simple View function. It will retrieve the factor -// associated with the (mandatory) address parameter it was provided with. -pub fn view_get_factor(_ctx: &ScViewContext, f: &GetFactorContext) { - - // Since we are sure that the 'address' parameter actually exists we can - // retrieve its actual value into an ScAddress value type. - let address: ScAddress = f.params.address().value(); - - // Create an ScImmutableMap proxy to the 'members' map in the state storage. - // Note that for views this is an *immutable* map as opposed to the *mutable* - // map we can access from the *mutable* state that gets passed to funcs. - let members: MapAddressToImmutableUint64 = f.state.members(); - - // Retrieve the factor associated with the address parameter. - let factor: u64 = members.get_uint64(&address).value(); - - // Set the factor in the results map of the function context. - // The contents of this results map is returned to the caller of the function. - f.results.factor().set_value(factor); -} -``` - - - - -```ts -// 'getFactor' is a simple View function. It will retrieve the factor -// associated with the (mandatory) address parameter it was provided with. -export function viewGetFactor( - ctx: wasmlib.ScViewContext, - f: sc.GetFactorContext, -): void { - // Since we are sure that the 'address' parameter actually exists we can - // retrieve its actual value into an ScAddress value type. - let address: wasmlib.ScAddress = f.params.address().value(); - - // Create an ScImmutableMap proxy to the 'members' map in the state storage. - // Note that for views this is an *immutable* map as opposed to the *mutable* - // map we can access from the *mutable* state that gets passed to funcs. - let members: sc.MapAddressToImmutableUint64 = f.state.members(); - - // Retrieve the factor associated with the address parameter. - let factor: u64 = members.getUint64(address).value(); - - // Set the factor in the results map of the function context. - // The contents of this results map is returned to the caller of the function. - f.results.factor().setValue(factor); -} -``` - - - - -The return values are passed to the caller through the predefined [Results](results.mdx) map -associated with the [Call Context](../../explanations/context.mdx). -Again, this works the same as for Funcs, although Funcs do not necessarily return values to the caller. The -[Schema Tool](usage.mdx) will generate a function-specific [Results](results.mdx) -structure with type-safe proxies to the result fields in this map. diff --git a/docs/content/developer/iota-evm/schema/how-tos/yaml.mdx b/docs/content/developer/iota-evm/schema/how-tos/yaml.mdx deleted file mode 100644 index 30368b2cbc8..00000000000 --- a/docs/content/developer/iota-evm/schema/how-tos/yaml.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: the syntax of a schema definition file will be described here. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - schema ---- - -# YAML Schema Definition: Level 1 Attributes - -The schema definition file can have the following level 1 attributes: - -## name - -- **Type**: Single string -- **Usage**: Dictates the package name for the smart contract - -## description - -- **Type**: Single string -- **Usage**: Describes the smart contract's functionality (currently not utilized in the final smart contract) - -## events - -- **Type**: Map of strings -- **Usage**: Define structured events ([more info](./events.mdx)) -- **Restriction**: Field data types must be primitive; arrays, maps, or typedefs are not allowed - -## structs - -- **Type**: Map of string maps -- **Usage**: Declare structs for future development, usable in schema definitions -- **Restriction**: Fields must hold primitive types; arrays, maps, or typedef aliases are prohibited - -## typedefs - -- **Type**: Map of strings -- **Usage**: Create aliases for primitive values; supports primitive values, maps of primitive values, or arrays of primitive values -- **Restriction**: Nested typedefs are not permissible - -## state - -- **Type**: Map of strings -- **Usage**: Contains key/value pairs for use-case specific data ([details](../../explanations/states.md)) -- **Note**: To employ nested types, create a typedef alias for arrays or maps, but ensure map keys are primitive - -## funcs & views - -Describe functions and views sharing the same parameter and result names, ensuring they adhere to identical data types. The attributes common to both are: - -- **`access`** -- **Requirement**: Must be a state variable -- **Details**: Defines access permissions ([read more](./access.mdx)) - -- **`params`** -- **Type**: Can vary — array, map, or typedef alias -- **Usage**: Specifies input parameters - -- **`results`** -- **Type**: Can vary — array, map, or typedef alias -- **Usage**: Designates return values - -### Special Note on `funcs` - -- **Reserved Keyword**: `init` — relates to a distinct function ([explore](./init.mdx)) diff --git a/docs/content/developer/iota-evm/schema/introduction.mdx b/docs/content/developer/iota-evm/schema/introduction.mdx deleted file mode 100644 index e36ebba7f9c..00000000000 --- a/docs/content/developer/iota-evm/schema/introduction.mdx +++ /dev/null @@ -1,62 +0,0 @@ -# The Schema Tool - -Smart contracts need robustness, combining both flexibility and stringent regulation to prevent mistakes and foster efficiency. -Using the Schema Tool ensures consistency, and simplifies smart contract development. - -## Why Use the Schema Tool? - -### Ensure Robustness and Consistency - -By employing a code generator rooted in a schema definition file, you achieve: - -- **Reliability**: Through debugged, trustworthy generated code. -- **Adaptability**: Facilitating modifications in the smart contract interface. -- **Intent Reflection**: Enforcing defined smart contract behavior at compile-time. -- **Multilingual Support**: Accommodating various programming languages. - -### Prevent Repetitive Coding - -Initial experiences illustrated repetitive coding in: - -- `on_load` function setup and maintenance. -- Function access rights verification. -- Function parameter type and presence confirmation. -- Establishing access to State, Params, and Results maps. -- Common string constant definitions. - -The schema tool diminishes redundancy and ensures up-to-date functionalities. - -## The Schema Definition File - -### Defining a Clear Interface - -The schema definition file serves as a single source of truth, encompassing crucial details like: - -- State Storage Variables. -- [Funcs and Views](../explanations/context.mdx). -- [Access rights](how-tos/access.mdx). -- [Input parameters](how-tos/params.mdx) and [output results](how-tos/results.mdx) . -- Additional Data Structures. - -### Automation with Schema Tool - -Using the [Schema Tool](how-tos/usage.mdx), the file facilitates: - -- **Skeleton Generation**: Crafting a smart contract framework that needs function implementations. -- **Interface Generation**: Creating interfaces to functions with stringent compile-time type-checking, thereby reducing errors. - -## Benefits of Schema Tool in Smart Contracts - -### Centralized Information - -Having all pertinent details in one place allows: - -- **Uniform Function Calls**: Through a generated interface used by client-side code. -- **Error Minimization**: Via compile-time type-checking. - -### Efficiency and Simplicity - -Knowledge of all essential aspects leads to: - -- **Constant Generation**: Avoiding typo-prone key strings repetition and precalculating essential values like `Hnames`. -- **Informative Code**: Generating code to correctly notify the host about available `Funcs` and `Views`. diff --git a/docs/content/developer/iota-evm/schema/proxies.mdx b/docs/content/developer/iota-evm/schema/proxies.mdx deleted file mode 100644 index 39c52b2f2c7..00000000000 --- a/docs/content/developer/iota-evm/schema/proxies.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: As there is no way for the Wasm code to access any memory outside its - own memory space, the WasmLib interface provides a number of proxies to make accessing - data within the ISC sandbox as seamless as possible. -image: /img/iota-evm/wasm_vm/Proxies.png -tags: - - how-to - - evm - - schema ---- -# Data Access Proxies - -To interact seamlessly with the ISC sandbox's regulated environment and facilitate smart contract data access, -the WasmLib introduces data access proxies. -Proxies are objects that can perform the underlying data transfers between the -separate systems. - -## Overview - -The restrictive ISC sandbox environment requires an intermediary, or a library, to access sandbox functionalities — a role that the WasmLib fulfills through data access `proxies`. -These entities stand as bridges, enabling data transfers between segregated systems, providing a streamlined pathway to interact with smart contract data stored on the ISC host. - -## The Role of Proxies - -Proxies refer to actual objects or values housed in the ISC host, -essentially acting as data references that understand how to manipulate the data they represent. -At the core, data is secured in maps on the ISC host with byte strings serving as both keys and values. - -The WasmLib recognizes three predefined maps: - -- **[State Map](../schema/how-tos/state.mdx):** A repository for all state storage values. -- **[Params Map](../schema/how-tos/params.mdx):** Holds the parameter values of the current function call. -- **[Results Map](../schema/how-tos/results.mdx):** Returns the result values of the function call. - -Through these maps, a complex, -JSON-like data structure can be created with the aid of the [Schema Tool](../schema/how-tos/usage.mdx), -although, fundamentally, this structure is rooted in simple key-value access on the underlying map. - -## Proxy Varieties - -Proxies are segmented into various categories to facilitate different functionalities, including value proxies and container proxies. - -### Value Proxies - -Representing a single instance of a predetermined data type, -value proxies are basic entities facilitating type conversion of the byte string representing the stored value. - -The [Schema Tool](../schema/how-tos/usage.mdx) ensures type-safe code generation, always selecting the suitable proxy type. - -### Container Proxies - -Container proxies offer a virtual nesting system on the underlying map, -creating paths leading to value proxies housed in a virtual container. -They use JSON and YAML nesting patterns, and there are two primary types: - -#### 1. Map Proxies - -- **Root Maps:** Encase any element type but restrict keys to human-readable strings, which are defined in the schema definition file. -- **Virtual Maps:** Resides under a root map and accommodates values of a single associated data type. - -#### 2. Array Proxies - -- Operate as a specialized map where the key is an Int32 value, forming a sequence from 0 to N-1 for an array harboring N elements. - -## Proxies in Action - -![Proxies in WasmLib](/img/iota-evm/wasm_vm/Proxies.png) - -In the illustration, we see the key-value combinations (Key 1 to Key 4) in the ISC state storage map. Key 4 directs us to an array containing indexed values ranging from 0 to N. - -Notice the precise reflection of these elements in the WasmLib proxies, -which maintain a container proxy for every container and a value proxy for each stored value. -However, it is not obligatory for a smart contract function to define a proxy for every value or container in the ISC state storage. diff --git a/docs/content/developer/iota-evm/solo/getting-started.md b/docs/content/developer/iota-evm/solo/getting-started.md deleted file mode 100644 index 0566990e16b..00000000000 --- a/docs/content/developer/iota-evm/solo/getting-started.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -description: Solo is a testing framework that allows developers to validate real smart contracts and entire inter-chain protocols. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - -# Testing Smart Contracts with Solo - -## What is Solo? - -[_Solo_](https://github.com/iotaledger/wasp/tree/develop/packages/solo) is a testing framework that allows developers to -validate real smart contracts and entire inter-chain protocols before deploying them on the distributed network. - -## Installation - -### Prerequisites - -[Go (version 1.20)](https://tip.golang.org/doc/go1.20). As _Solo_ tests are written in Go, you must -[install Go](https://go.dev/doc/install). - -### Access the Solo Framework - -You can access the Solo package by cloning the [Wasp repository](#clone-the-wasp-repository) -or [installing the Solo package](#install-the-solo-package). - -#### Clone the Wasp Repository - -_Solo_ is part of the [_Wasp_ codebase repository](https://github.com/iotaledger/wasp.git). You can access the Solo -framework by cloning the repository with the following command: - -```shell -git clone https://github.com/iotaledger/wasp.git -``` - -After you have cloned the repository, you can access the Solo package in the `/packages/solo` folder. - -#### Install the Solo Package - -You can install the Solo package separately using the following command: - -```shell -go get github.com/iotaledger/wasp/packages/solo -``` - -:::tip Go Docs - -You can browse the Solo Go API reference (updated to the `master` branch) in -[go-docs](https://pkg.go.dev/github.com/iotaledger/wasp/packages/solo). - -::: - -### Example Contracts - -You will need a smart contract to test along with Solo. -You can find example implementations of Wasm smart contracts, including source code and tests, in the Wasp -repository’s [contracts/wasm folder](https://github.com/iotaledger/wasp/tree/develop/contracts/wasm). - -The following sections will present some Solo usage examples. You can find the example code in -the [Wasp repository](https://github.com/iotaledger/wasp/tree/develop/documentation/tutorial-examples). - -### Run `*_test` Files - -You can run `*_test` files by moving to their directory and running the following command: - -```shell -go test -``` - -If you run this command from the `/documentation/tutorial-examples` folder, you will run the -[Tutorial Test](https://github.com/iotaledger/wasp/blob/develop/documentation/tutorial-examples/test/tutorial_test.go), -which contains all the examples explained in the following sections. diff --git a/docs/content/developer/iota-evm/solo/how-tos/deploying-sc.md b/docs/content/developer/iota-evm/solo/how-tos/deploying-sc.md deleted file mode 100644 index a74d1dbac63..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/deploying-sc.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -description: Deploying Wasm smart contracts with Solo. -image: /img/iota-evm/tutorial/send_request.png -tags: - - how-to - - evm - - solo - - testing ---- - -# Deploying Wasm Smart Contracts - -:::note WASM VM - -For more information about how to create Wasm smart contracts, refer to the [Wasm VM chapter](../../getting-started/languages-and-vms.mdx#wasm-vm-for-isc). - -::: - -## Deploy the Solo Tutorial - -The following examples will make use of the -[`solotutorial` Rust/Wasm smart contract](https://github.com/iotaledger/wasp/tree/develop/documentation/tutorial-examples). - -In order to test the smart contract using Solo, first you need to deploy it. You can use the following code to -deploy `solotutorial_bg.wasm`: - -```go -func TestTutorialDeploySC(t *testing.T) { - env := solo.New(t, &solo.InitOptions{AutoAdjustStorageDeposit: true}) - chain := env.NewChain() - err := chain.DeployWasmContract(nil, "solotutorial", "solotutorial_bg.wasm") - require.NoError(t, err) -} -``` - -This will work as long as the `solotutorial_bg.wasm` file is in the same directory as the Go test code. - -### Create a Default Wallet and Chain - -You can use the `NewChain()` function to create a new wallet and deploys a new chain using said wallet, and other -default parameters. You can access the wallet calling `chain.OriginatorPrivateKey`. - -### DeployWasmContract Parameters - -#### Deployer's Key Pair - -The first parameter to `DeployWasmContract` is the key pair of the deployer of the smart contract. You can pass `nil` -to use a default wallet, which can be accessed as `chain.OriginatorPrivateKey`. - -#### Smart Contract Name - -The second parameter to `DeployWasmContract` (`"solotutorial"`), is the name assigned to the smart contract instance. -Smart contract instance names must be unique across each chain. - -### AutoAdjustStorageDeposit - -In the example above we enabled the `AutoAdjustStorageDeposit` option. -This is necessary in order to automatically adjust all sent L1 transactions to include the storage deposit if -necessary (provided that the sender owns the funds). - -It is possible to disable the option and have manual control of the storage deposit, but in that case the deployment -of the smart contract will have to be done "by hand". - -In most cases it is recommended to leave it enabled. diff --git a/docs/content/developer/iota-evm/solo/how-tos/error-handling.md b/docs/content/developer/iota-evm/solo/how-tos/error-handling.md deleted file mode 100644 index 1056b5b004a..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/error-handling.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: What happens when a smart contract invocation fails? -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - -# Error Handling - -The following test posts a request to the `solotutorial` smart contract without the expected parameter `"str"`, causing -the smart contract call to panic: - -```go -func TestTutorialInvokeSCError(t *testing.T) { - env := solo.New(t, &solo.InitOptions{AutoAdjustStorageDeposit: true}) - chain := env.NewChain() - err := chain.DeployWasmContract(nil, "solotutorial", "solotutorial_bg.wasm") - require.NoError(t, err) - - // missing the required parameter "str" - req := solo.NewCallParams("solotutorial", "storeString"). - WithMaxAffordableGasBudget() - - _, err = chain.PostRequestSync(req, nil) - require.Error(t, err) - require.True(t, err.Error() == "WASM: panic in VM: missing mandatory string") -} -``` - -The `_, err = chain.PostRequestSync(req, nil)` will return an error containing `WASM: panic in VM: missing mandatory string`. - -This shows that the request resulted in a panic. -The Solo test passes because of the `require.Error(t, err)` line. - -Note that this test still ends with the state `#4`, although the last request to the smart contract failed: - -```log -20:09.974258867 INFO TestTutorialInvokeSCError.ch1 solo/run.go:156 state transition --> #4. Requests in the block: 1. Outputs: 1 -``` - -This shows that a chain block is always generated, regardless of whether the smart contract call succeeds or not. The -result of the request is stored in the chain's [`blocklog`](../../../../references/iota-evm/core-contracts/blocklog.md) in the form of -a receipt. In fact, the received Go error `err` in the test above is just generated from the request receipt. - -If a panic occurs during a smart contract call, it is recovered by the VM context, and the request is marked as failed. -Any state changes made prior to the panic are rolled back. diff --git a/docs/content/developer/iota-evm/solo/how-tos/examples.mdx b/docs/content/developer/iota-evm/solo/how-tos/examples.mdx deleted file mode 100644 index 0afd7cf05ca..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/examples.mdx +++ /dev/null @@ -1,274 +0,0 @@ ---- -description: Use the SoloContext to create full-blown tests for the dividend example smart contract. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - - -# Example Tests - -In the previous sections we showed how you can [`call()`](../../schema/how-tos/call.mdx) or -[`post()`](../../schema/how-tos/post.mdx) smart contract function requests. We also created a few wrapper -functions to simplify calling these functions even further. Now we will look at how to use -the SoloContext to create full-blown tests for the `dividend` example smart contract. - -Let's start with a simple test. We are going to use the `member` function to add a valid -new member/factor combination to the member group. - - - - - -```go -func TestAddMemberOk(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member1 := ctx.NewSoloAgent("member1") - dividendMember(ctx, member1, 100) - require.NoError(t, ctx.Err) -} -``` - - - - -The above test first deploys the `dividend` smart contract to a new chain, and returns a -SoloContext `ctx`. Then it uses `ctx` to create a new SoloAgent `member1`. When creating a -SoloAgent a new _Tangle_ address is created for that agent and its on-chain account is -pre-seeded with 10M base tokens so that it is ready to be used in tests. The SoloAgent can -be used whenever an address or agent ID needs to be provided, it can be used to sign a -request to identify it as the sender, and it can be used to inspect the asset balances on -its Tangle address and its on-chain account. - -In this case, we simply create `member1`, and pass it to the dividend contract's `member` -function, which will receive the address of `member1` and a dispersal factor of 100. -Finally, we check if ctx has received an error during the execution of the call. Remember -that the only way to pass an error from a WasmLib function call is through a `panic()` -call. The code that handles the actual call will intercept any `panic()` that was raised, -and return an error. The SoloContext saves this error for later inspection, because the -function descriptor used in the call itself has no way of passing back this error. - -The next two example tests each call the contract's `member` function in the exact same -way, but in both cases one required parameter is omitted. The idea is to test that the -function properly panics by checking the ctx.Err value is not nil after the call. Finally, -the error message text is checked to see if it contains the expected error message. - - - - - -```go -func TestAddMemberFailMissingAddress(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member := dividend.ScFuncs.Member(ctx) - member.Params.Factor().SetValue(100) - member.Func.Post() - require.Error(t, ctx.Err) - require.Contains(t, ctx.Err.Error(), "missing mandatory address") -} - -func TestAddMemberFailMissingFactor(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member1 := ctx.NewSoloAgent("member1") - member := dividend.ScFuncs.Member(ctx) - member.Params.Address().SetValue(member1.ScAgentID().Address()) - member.Func.Post() - require.Error(t, ctx.Err) - require.Contains(t, ctx.Err.Error(), "missing mandatory factor") -} -``` - - - - -Each test has to set up the chain/contract/context from scratch. We will often use a -specific setupTest() function to do all setup work that is shared by many tests. - -We cannot use the `dividendMember` wrapper function in these two tests because of the -missing required function parameters. So we have simply copy/pasted the code, and removed -the `Params` proxy initialization lines that we wanted to be detected as missing. - -Now let's see a more complex example: - - - - - -```go -func TestDivide1Member(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member1 := ctx.NewSoloAgent("member1") - bal := ctx.Balances(member1) - - dividendMember(ctx, member1, 100) - require.NoError(t, ctx.Err) - - bal.Chain += ctx.GasFee - bal.Originator += ctx.StorageDeposit - ctx.GasFee - bal.VerifyBalances(t) - - const dividendToDivide = 1*isc.Million + 1 - dividendDivide(ctx, dividendToDivide) - require.NoError(t, ctx.Err) - - bal.Chain += ctx.GasFee - bal.Originator -= ctx.GasFee - bal.Add(member1, dividendToDivide) - bal.VerifyBalances(t) -} -``` - - - - -The first half of the code is almost identical to our first test above. We set up the -test, create an agent, set the factor for that agent to 100, and verify that no error -occurred. Notice how we additionally call `ctx.Balances()` to make a snapshot of all the -original asset balances including those of the agent. - -Then in the next lines we update the asset balances with the changes we expect to happen -because of the call to the `member` function. And then we verify these changes against the -actual asset balances by calling `bal.VerifyBalances(t)`. - -Next we transfer 1M + 1 tokens as part of the post request to the `divide` function. We -subsequently check that no error has occurred. Finally, we again modify the asset balances -to reflect the expected changes and verify these changes against the actual asset -balances. - -Now let's skip to the most complex test of all: - - - - - -```go - func TestDivide3Members(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member1 := ctx.NewSoloAgent("member1") - bal := ctx.Balances(member1) - - dividendMember(ctx, member1, 25) - require.NoError(t, ctx.Err) - - bal.Chain += ctx.GasFee - bal.Originator += ctx.StorageDeposit - ctx.GasFee - bal.VerifyBalances(t) - - member2 := ctx.NewSoloAgent("member2") - bal = ctx.Balances(member1, member2) - - dividendMember(ctx, member2, 50) - require.NoError(t, ctx.Err) - - bal.Chain += ctx.GasFee - bal.Originator += ctx.StorageDeposit - ctx.GasFee - bal.VerifyBalances(t) - - member3 := ctx.NewSoloAgent() - bal = ctx.Balances(member1, member2, member3) - - dividendMember(ctx, member3, 75) - require.NoError(t, ctx.Err) - - bal.Chain += ctx.GasFee - bal.Originator += ctx.StorageDeposit - ctx.GasFee - bal.VerifyBalances(t) - - const dividendToDivide = 2*isc.Million - 1 - dividendDivide(ctx, dividendToDivide) - require.NoError(t, ctx.Err) - - remain := dividendToDivide - dividendToDivide*25/150 - dividendToDivide*50/150 - dividendToDivide*75/150 - bal.Chain += ctx.GasFee - bal.Originator += remain - ctx.GasFee - bal.Add(member1, dividendToDivide*25/150) - bal.Add(member2, dividendToDivide*50/150) - bal.Add(member3, dividendToDivide*75/150) - bal.VerifyBalances(t) - - const dividendToDivide2 = 2*isc.Million + 234 - dividendDivide(ctx, dividendToDivide2) - require.NoError(t, ctx.Err) - - remain = dividendToDivide2 - dividendToDivide2*25/150 - dividendToDivide2*50/150 - dividendToDivide2*75/150 - bal.Chain += ctx.GasFee - bal.Originator += remain - ctx.GasFee - bal.Add(member1, dividendToDivide2*25/150) - bal.Add(member2, dividendToDivide2*50/150) - bal.Add(member3, dividendToDivide2*75/150) - bal.VerifyBalances(t) -} -``` - - - - -This function creates 3 agents, and associates factors of 25, 50, and 75 respectively to -them. That required 3 `member` requests, and we verify the expected balance changes after -each request. Next the `divide` function is called, with 2M-1 tokens passed to it. - -After this we verify that each agent has been distributed tokens according to its relative -factor. Those factors are 25/150th, 50/150th, and 75/150th, respectively. Note that we -cannot transfer fractional tokens, so we truncate to the nearest integer and ultimately -any remaining tokens are not transferred, but remain in the sender's account. - -Finally, we call `divide` again with 2M+234 tokens and again verify the asset balances -afterwards. - -Next we will show how to test [Views](../../schema/how-tos/views.mdx) and/or [Funcs](../../schema/how-tos/funcs.mdx) that return -a result. Note that even though Funcs are usually invoked through a [`post()`](../../schema/how-tos/post.mdx) -request, in which case any return values are inaccessible, they still can be invoked -through a [call()](../../schema/how-tos/call.mdx) from within another Func, in which these return values can -be used normally. Since solo executes [`post()`](../../schema/how-tos/post.mdx) requests synchronously, it is -possible to have a Func return a result and test for certain result values. - - - - - -```go -func TestGetFactor(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - - member1 := ctx.NewSoloAgent("member1") - dividendMember(ctx, member1, 25) - require.NoError(t, ctx.Err) - - member2 := ctx.NewSoloAgent("member2") - dividendMember(ctx, member2, 50) - require.NoError(t, ctx.Err) - - member3 := ctx.NewSoloAgent() - dividendMember(ctx, member3, 75) - require.NoError(t, ctx.Err) - - value := dividendGetFactor(ctx, member3) - require.NoError(t, ctx.Err) - require.EqualValues(t, 75, value) - - value = dividendGetFactor(ctx, member2) - require.NoError(t, ctx.Err) - require.EqualValues(t, 50, value) - - value = dividendGetFactor(ctx, member1) - require.NoError(t, ctx.Err) - require.EqualValues(t, 25, value) -} -``` - - - - -Here we first set up the same 3 dispersion factors, and then we retrieve the dispersion -factors for each member in reverse order and verify their values. - -In the [next section](timelock.mdx) we will describe a few more helper member functions -of the SoloContext. diff --git a/docs/content/developer/iota-evm/solo/how-tos/first-example.md b/docs/content/developer/iota-evm/solo/how-tos/first-example.md deleted file mode 100644 index 57d3a74ef41..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/first-example.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -description: Example of a _Solo_ test. It deploys a new chain and invokes some view calls. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - -# First Example - -The following is an example of a _Solo_ test. It deploys a new chain and invokes some view calls in the -[`root`](../../../../references/iota-evm/core-contracts/root.md) and [`governance`](../../../../references/iota-evm/core-contracts/governance.md) -[core contracts](../../../../references/iota-evm/core-contracts/overview.md). - -```go -import ( - "testing" - - "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/vm/core/corecontracts" - "github.com/stretchr/testify/require" -) - -func TestTutorialFirst(t *testing.T) { - env := solo.New(t) - chain := env.NewChain() - - // calls views governance::ViewGetChainInfo and root:: ViewGetContractRecords - chainID, chainOwnerID, coreContracts := chain.GetInfo() - // assert that all core contracts are deployed - require.EqualValues(t, len(corecontracts.All), len(coreContracts)) - - t.Logf("chain ID: %s", chainID.String()) - t.Logf("chain owner ID: %s", chainOwnerID.String()) - for hname, rec := range coreContracts { - t.Logf(" Core contract %q: %s", rec.Name, hname) - } -} -``` - -The output of the test will be something like this: - -```log -=== RUN TestTutorialFirst -29:43.383770108 INFO TestTutorialFirst.db dbmanager/dbmanager.go:64 creating new in-memory database for: CHAIN_REGISTRY -29:43.383957435 INFO TestTutorialFirst solo/solo.go:162 Solo environment has been created: logical time: 00:01.001000000, time step: 1ms -29:43.384671943 INFO TestTutorialFirst solo/solo.go:236 deploying new chain 'tutorial1'. ID: tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd, state controller address: tgl1qpk70349ftcpvlt6lnn0437p63wt7w2ejvlkw93wkkt0kc39f2wpvuv73ea -29:43.384686865 INFO TestTutorialFirst solo/solo.go:238 chain 'tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd'. state controller address: tgl1qpk70349ftcpvlt6lnn0437p63wt7w2ejvlkw93wkkt0kc39f2wpvuv73ea -29:43.384698704 INFO TestTutorialFirst solo/solo.go:239 chain 'tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd'. originator address: tgl1qq93jh7dsxq3lznajgtq33v26rt0pz0rs0rwar4jahahp6h2hh9jy4nc52k -29:43.384709967 INFO TestTutorialFirst.db dbmanager/dbmanager.go:64 creating new in-memory database for: tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd -29:43.384771911 INFO TestTutorialFirst solo/solo.go:244 chain 'tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd'. origin state commitment: c4f09061cd63ea506f89b7cbb3c6e0984f124158 -29:43.417023624 INFO TestTutorialFirst solo/solo.go:171 solo publisher: state [tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd 1 1 0-6c7ff6bc5aaa3af12f9b6b7c43dcf557175ac251418df562f7ec4ff092e84d4f 0000000000000000000000000000000000000000000000000000000000000000] -29:43.417050354 INFO TestTutorialFirst solo/solo.go:171 solo publisher: request_out [tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd 0-11232aa47639429b83faf79547c6bf615bd65aa461f243c89e4073b792ac89b7 1 1] -29:43.417056290 INFO TestTutorialFirst.tutorial1 solo/run.go:156 state transition --> #1. Requests in the block: 1. Outputs: 1 -29:43.417179099 INFO TestTutorialFirst.tutorial1 solo/run.go:176 REQ: 'tx/0-11232aa47639429b83faf79547c6bf615bd65aa461f243c89e4073b792ac89b7' -29:43.417196814 INFO TestTutorialFirst.tutorial1 solo/solo.go:301 chain 'tutorial1' deployed. Chain ID: tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd - tutorial_test.go:20: chain ID: tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd - tutorial_test.go:21: chain owner ID: tgl1qq93jh7dsxq3lznajgtq33v26rt0pz0rs0rwar4jahahp6h2hh9jy4nc52k - tutorial_test.go:23: Core contract "blob": fd91bc63 - tutorial_test.go:23: Core contract "governance": 17cf909f - tutorial_test.go:23: Core contract "errors": 8f3a8bb3 - tutorial_test.go:23: Core contract "evm": 07cb02c1 - tutorial_test.go:23: Core contract "accounts": 3c4b5e02 - tutorial_test.go:23: Core contract "root": cebf5908 - tutorial_test.go:23: Core contract "blocklog": f538ef2b ---- PASS: TestTutorialFirst (0.03s) -``` - -:::note - -- The example uses [`stretchr/testify`](https://github.com/stretchr/testify) for assertions, but it is not strictly - required. -- Addresses, chain IDs and other hashes should be the same on each run of the test because Solo uses a constant seed by - default. -- The timestamps shown in the log come from the computer's timer, but the Solo environment operates on its own logical - time. - -::: - -The [core contracts](../../../../references/iota-evm/core-contracts/overview.md) listed in the log are automatically deployed on each -new chain. The log also shows their _contract IDs_. - -The output fragment in the log `state transition --> #1` means that the state of the chain has changed from block index -0 (the origin index of the empty state) to block index 1. State #0 is the empty origin state, and #1 always contains all -core smart contracts deployed on the chain, as well as other data internal to the chain itself, such as the _chainID_ -and the _chain owner ID_. - -The _chain ID_ and _chain owner ID_ are, respectively, the ID of the deployed chain, and the address of the L1 account -that triggered the deployment of the chain (which is automatically generated by Solo in our example, but it can be -overridden when calling `env.NewChain`). diff --git a/docs/content/developer/iota-evm/solo/how-tos/invoking-sc.md b/docs/content/developer/iota-evm/solo/how-tos/invoking-sc.md deleted file mode 100644 index 4922a23d45a..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/invoking-sc.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -description: Invoking smart contracts with on-ledger and off-ledger requests with Solo. -image: /img/iota-evm/tutorial/send_request.png -tags: - - how-to - - on-ledger - - off-ledger - - evm - - solo - - testing ---- - -# Invoking Smart Contracts - -After deploying -the [`solotutorial`](https://github.com/iotaledger/wasp/tree/develop/documentation/tutorial-examples) -smart contract, you can invoke the `storeString` function: - -```go -func TestTutorialInvokeSC(t *testing.T) { - env := solo.New(t, &solo.InitOptions{AutoAdjustStorageDeposit: true}) - chain := env.NewChain() - err := chain.DeployWasmContract(nil, "solotutorial", "solotutorial_bg.wasm") - require.NoError(t, err) - - // invoke the `storeString` function - req := solo.NewCallParams("solotutorial", "storeString", "str", "Hello, world!"). - WithMaxAffordableGasBudget() - _, err = chain.PostRequestSync(req, nil) - require.NoError(t, err) - - // invoke the `getString` view - res, err := chain.CallView("solotutorial", "getString") - require.NoError(t, err) - require.Equal(t, "Hello, world!", codec.MustDecodeString(res.MustGet("str"))) -} -``` - -## Parameters - -### `NewCallParams()` - -The above example uses `NewCallParams` to set up the parameters of the request that it will send to the contract. -It specifies that it wants to invoke the `storeString` entry point of the `solotutorial` smart contract, passing the -parameter named `str` with the string value `"Hello, world!"`. - -### `WithMaxAffordableGasBudget()` - -`WithMaxAffordableGasBudget()` assigns the gas budget of the request to the maximum that the sender can afford with the -funds they own on L2 (including any funds attached in the request itself). -In this case the funds attached automatically for the storage deposit will be enough to cover for the gas fee, so it is -not necessary to manually deposit more funds for gas. - -## `PostRequestSync()` - -`PostRequestSync` sends an on-ledger request to the chain. - -## On-Ledger Requests - -[![Generic process of posting an on-ledger request to the smart contract](/img/iota-evm/tutorial/send_request.png)](/img/iota-evm/tutorial/send_request.png) - -The diagram above depicts the generic process of posting an _on-ledger_ request to the smart contract. -The same diagram is valid for the Solo environment and any other requester that sends an on-ledger request, e.g., the -IOTA Smart Contracts wallet or another chain. - -Posting an on-ledger request always consists of the steps below. -Note that in Solo, all seven steps are carried out by a single call to `PostRequestSync`. - -1. Create the L1 transaction, which wraps the L2 request and moves tokens. - - Each on-ledger request must be contained in a transaction on the ledger. - Therefore, it must be signed by the sender’s private key. - This securely identifies each requester in IOTA Smart Contracts. - In Solo, the transaction is signed by the private key provided in the second parameter of the `PostRequestSync` call - (`chain.OriginatorPrivateKey()` by default). - -2. Post and confirm the transaction to the L1 ledger. - - In Solo, it is just adding the transaction to the emulated L1 ledger, so it is confirmed immediately and - synchronously. - The confirmed transaction on the ledger becomes part of the backlog of requests to be processed by the chain. - In the real L1 ledger, the sender must wait until the ledger confirms the transaction. - -3. The chain picks the request from the backlog and runs the request on the VM. -4. The _VM_ calls the target entry point of the smart contract program. The program updates the state. -5. The VM produces a state update transaction (the _anchor_). -6. The chain signs the transaction with its private key (the `chain.StateControllerKeyPair()` in Solo). -7. The chain posts the resulting transaction to the L1 ledger and, after confirmation, commits the corresponding state. - -The following lines in the test log correspond to step 7: - -```log -49:37.771863573 INFO TestTutorialInvokeSC solo/solo.go:171 solo publisher: state [tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd 4 1 0-177c8a62feb7d434608215a179dd6637b8038d1237dd264 -d8feaf4d9a851b808 0000000000000000000000000000000000000000000000000000000000000000] -49:37.771878833 INFO TestTutorialInvokeSC solo/solo.go:171 solo publisher: request_out [tgl1pzehtgythywhnhnz26s2vtpe2wy4y64pfcwkp9qvzhpwghzxhwkps2tk0nd 0-c55b41b07687c644b7f7a1b9fb5da86f2d40195f39885 -bc348767e2dd285ca15 4 1] -49:37.771884127 INFO TestTutorialInvokeSC.ch1 solo/run.go:156 state transition --> #4. Requests in the block: 1. Outputs: 1 -``` - -## Off-ledger Requests - -Alternatively, you could send an off-ledger request by using `chain.PostRequestOffLedger` instead of `PostRequestSync`. -However, since off-ledger requests cannot have tokens attached, in order to cover the gas fee, you must deposit funds to -the chain beforehand: - -```go -user, _ := env.NewKeyPairWithFunds(env.NewSeedFromIndex(1)) -chain.DepositBaseTokensToL2(10_000, user) // to cover gas fees -_, err = chain.PostRequestOffLedger(req, user) -require.NoError(t, err) -``` diff --git a/docs/content/developer/iota-evm/solo/how-tos/test.mdx b/docs/content/developer/iota-evm/solo/how-tos/test.mdx deleted file mode 100644 index 848156096f1..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/test.mdx +++ /dev/null @@ -1,117 +0,0 @@ ---- -description: Testing of smart contracts happens in the Solo testing environment. This enables synchronous, deterministic testing of smart contract functionality without the overhead of having to start nodes, set up a committee, and send transactions over the _Tangle_. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - - -# Testing Smart Contracts - -Testing of smart contracts happens in the [Solo](../getting-started.md) testing -environment. This enables synchronous, deterministic testing of smart contract -functionalities without the overhead of having to start nodes, set up a committee, and -send transactions over the _Tangle_. Instead, you can use Go's built-in test environment in -combination with Solo to deploy chains and smart contracts and simulate transactions. - -Solo directly interacts with the ISC code, and therefore uses all the ISC-specific data -types directly. Our Wasm smart contracts cannot access these types directly, because they -run in a separate, sandboxed environment. Therefore, WasmLib implements its -[own versions](../../../../references/iota-evm/wasm-lib-data-types.mdx) of these data types, and the _VM_ layer acts as a data type -translator between both systems. - -The impact of this type transformation used to be that to be able to write tests in the -solo environment the user also needed to know about the ISC-specific data types and type -conversion functions, and exactly how to properly pass such data in and out of smart -contract function calls. This burdened the user with an unnecessary increased learning -curve and countless unnecessary type conversions. - -With the introduction of the [Schema Tool](../../schema/how-tos/usage.mdx), we were able to remove this -impedance mismatch and allow the users to test smart contract functionality in terms of -the WasmLib data types and functions that they are already familiar with. To that end, we -introduced `SoloContext`, a new [Call Context](../../explanations/context.mdx) that specifically works with -the Solo testing environment. We aimed to simplify the testing of smart contracts as much -as possible, keeping the full Solo interface hidden as much as possible, but still -available when necessary. - -The only concession we still have to make is to the language used. Because Solo only works -in the Go language environment, we have to use the Go language version of the interface -code that the [Schema Tool](../../schema/how-tos/usage.mdx) generates when testing our smart contracts. Because -WasmLib programming for Go, Rust, and TypeScript are practically identical, we feel that -this is not unsurmountable. The WasmLib interfaces only differ slightly if language -idiosyncrasies force differences in syntax or naming conventions. This hurdle used to be a -lot bigger, when direct programming of Solo had to be used, and most type conversions had -to be done manually. Now we get to use the generated compile-time type-checked interface -to our smart contract functions that we are already familiar with. - -Let's look at the simplest way of initializing a smart contract by using the new -`SoloContext` in a test function: - - - - - -```go -func TestDeploy(t *testing.T) { - ctx := wasmsolo.NewSoloContext(t, dividend.ScName, dividend.OnDispatch) - require.NoError(t, ctx.ContractExists(dividend.ScName)) -} -``` - - - - -The first line will automatically create a new chain, and upload and deploy the provided -example `dividend` contract to this chain. It returns a `SoloContext` for further use. The -second line verifies the existence of the deployed contract on the chain associated with -that [Call Context](../../explanations/context.mdx). - -Here is another part of the `dividend` test code, where you can see how we wrap repetitive -calls to smart contract functions that are used in multiple tests: - - - - - -```go -func dividendMember(ctx *wasmsolo.SoloContext, agent *wasmsolo.SoloAgent, factor uint64) { - member := dividend.ScFuncs.Member(ctx) - member.Params.Address().SetValue(agent.ScAgentID().Address()) - member.Params.Factor().SetValue(factor) - member.Func.Post() -} - -func dividendDivide(ctx *wasmsolo.SoloContext, amount uint64) { - divide := dividend.ScFuncs.Divide(ctx) - divide.Func.TransferBaseTokens(amount).Post() -} - -func dividendGetFactor(ctx *wasmsolo.SoloContext, member *wasmsolo.SoloAgent) uint64 { - getFactor := dividend.ScFuncs.GetFactor(ctx) - getFactor.Params.Address().SetValue(member.ScAgentID().Address()) - getFactor.Func.Call() - value := getFactor.Results.Factor().Value() - return value -} -``` - - - - -As you can see, we pass the `SoloContext` and the parameters to the wrapper functions, -then use the `SoloContext` to create a [Function Descriptor](../../schema/how-tos/funcdesc.mdx) for the wrapped -function, pass any parameters through the [Params](../../schema/how-tos/params.mdx) proxy, and then either -[`post()`](../../schema/how-tos/post.mdx) the function request or [`call`](../../schema/how-tos/call.mdx) the function. Any -results returned are extracted through the descriptor's [Results](../../schema/how-tos/results.mdx) proxy, and -returned by the wrapper function. - -There is hardly any difference in the way the function interface is used within Wasm code -or within Solo test code. The [Call Context](../../explanations/context.mdx) knows how to properly -[`call()`](../../schema/how-tos/call.mdx) or [`post()`](../../schema/how-tos/post.mdx) the function call through the function -descriptor. This makes for seamless testing of smart contracts. - -In the [next section](examples.mdx) we will go deeper into how the helper member functions -of the SoloContext are used to simplify tests. diff --git a/docs/content/developer/iota-evm/solo/how-tos/the-l1-ledger.md b/docs/content/developer/iota-evm/solo/how-tos/the-l1-ledger.md deleted file mode 100644 index 47302b43680..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/the-l1-ledger.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -description: How to interact with the L1 ledger in Solo. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - -# The L1 Ledger - -IOTA Smart Contracts work as a **layer 2** (**L2**) extension of the _IOTA Multi-Asset Ledger_, **layer 1** (**L1**). -The specifics of the ledger is outside the scope of this documentation; for now it is sufficient to know that the ledger -contains balances of different kinds of assets (base tokens, native tokens, foundries and _NFT_s) locked in addresses. -Assets can only be moved on the ledger by unlocking the corresponding address with its private key. - -For example: - -```log -Address: iota1pr7vescn4nqc9lpvv37unzryqc43vw5wuf2zx8tlq2wud0369hjjugg54mf - IOTA: 4012720 - Native token 0x08fcccc313acc182fc2c647dc98864062b163a8ee254231d7f029dc6be3a2de52e0100000000: 100 - NFT 0x94cd51b79d9608ed6e38780d48e9fc8c295b893077739b28ce591c45b33dec44 -``` - -In this example, the address owns some base tokens (IOTA), 100 units of a native token with ID `0x08fc...`, and an NFT -with ID `0x94cd...`. - -You can find more information about the ledger in the -[Multi-Asset Ledger TIP](https://github.com/lzpap/tips/blob/master/tips/TIP-0018/tip-0018.md). - -In normal operation, the L2 state is maintained by a committee of Wasp _nodes_. The L1 ledger is provided and -maintained by a network of [Hornet](https://github.com/iotaledger/hornet) nodes, which is a distributed implementation -of the IOTA Multi-Asset Ledger. - -The Solo environment implements a standalone in-memory ledger, simulating the behavior of a real L1 ledger without the -need to run a network of Hornet nodes. - -The following example creates a new wallet (private/public key pair) and requests some base tokens from the faucet: - -```go -func TestTutorialL1(t *testing.T) { - env := solo.New(t) - _, userAddress := env.NewKeyPairWithFunds(env.NewSeedFromIndex(1)) - t.Logf("address of the user is: %s", userAddress.Bech32(parameters.L1.Protocol.Bech32HRP)) - numBaseTokens := env.L1BaseTokens(userAddress) - t.Logf("balance of the user is: %d base tokens", numBaseTokens) - env.AssertL1BaseTokens(userAddress, utxodb.FundsFromFaucetAmount) -} -``` - -The _output_ of the test is: - -```log -=== RUN TestTutorialL1 -47:49.136622566 INFO TestTutorialL1.db dbmanager/dbmanager.go:64 creating new in-memory database for: CHAIN_REGISTRY -47:49.136781104 INFO TestTutorialL1 solo/solo.go:162 Solo environment has been created: logical time: 00:01.001000000, time step: 1ms - tutorial_test.go:32: address of the user is: tgl1qp5d8zm9rr9rcae2hq95plx0rquy5gu2mpedurm2kze238neuhh5csjngz0 - tutorial_test.go:34: balance of the user is: 1000000000 base tokens ---- PASS: TestTutorialL1 (0.00s) -``` - -The L1 ledger in Solo can be accessed via the Solo instance called `env`. -The ledger is unique for the lifetime of the Solo environment. -Even if several L2 chains are deployed during the test, all of them will live on the same L1 ledger; this way Solo makes -it possible to test cross-chain transactions. -(Note that in the test above we did not deploy any chains: the L1 ledger exists independently of any chains.) diff --git a/docs/content/developer/iota-evm/solo/how-tos/the-l2-ledger.md b/docs/content/developer/iota-evm/solo/how-tos/the-l2-ledger.md deleted file mode 100644 index 86c90e654a9..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/the-l2-ledger.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: Smart contracts can exchange assets between themselves on the same chain and between different chains, as well as with addresses on the L1 ledger. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - -# The L2 Ledger - -Each chain in IOTA Smart Contracts contains its own L2 ledger, independent of the L1 ledger. -Smart contracts can exchange assets between themselves on the same chain, between different chains, and with addresses -on the L1 Ledger. - -Imagine that you have a wallet with some tokens on the L1 ledger, and you want to send those tokens to a smart contract -on a chain and later receive these tokens back on L1. - -On the L1 ledger, your wallet's private key is represented by an address, which holds some tokens. -Those tokens are _controlled_ by the private key. - -In IOTA Smart Contracts the L2 ledger is a collection of _on-chain accounts_ (sometimes also called just _accounts_). -Each L2 account is controlled by the same private key as its associated address and can hold tokens on the chain's -ledger, just like an address can hold tokens on L1. -This way, the chain is essentially a custodian of the tokens deposited in its accounts. - -## Deposit and Withdraw Tokens - -The following test demonstrates how a wallet can deposit tokens in a chain -account and then withdraw them. - -Note that the math is made somewhat more complex by the gas fees and storage deposit. -You could ignore them, but we include them in the example to show you exactly how you can handle them. - -```go -func TestTutorialAccounts(t *testing.T) { - env := solo.New(t, &solo.InitOptions{AutoAdjustStorageDeposit: true}) - chain := env.NewChain() - - // create a wallet with some base tokens on L1: - userWallet, userAddress := env.NewKeyPairWithFunds(env.NewSeedFromIndex(0)) - env.AssertL1BaseTokens(userAddress, utxodb.FundsFromFaucetAmount) - - // the wallet can we identified on L2 by an AgentID: - userAgentID := isc.NewAgentID(userAddress) - // for now our on-chain account is empty: - chain.AssertL2BaseTokens(userAgentID, 0) - - // send 1 Mi from the L1 wallet to own account on-chain, controlled by the same wallet - req := solo.NewCallParams(accounts.Contract.Name, accounts.FuncDeposit.Name). - AddBaseTokens(1 * isc.Million) - - // estimate the gas fee and storage deposit - gas1, gasFee1, err := chain.EstimateGasOnLedger(req, userWallet, true) - require.NoError(t, err) - storageDeposit1 := chain.EstimateNeededStorageDeposit(req, userWallet) - require.Zero(t, storageDeposit1) // since 1 Mi is enough - - // send the deposit request - req.WithGasBudget(gas1). - AddBaseTokens(gasFee1) // including base tokens for gas fee - _, err = chain.PostRequestSync(req, userWallet) - require.NoError(t, err) - - // our L1 balance is 1 Mi + gas fee short - env.AssertL1BaseTokens(userAddress, utxodb.FundsFromFaucetAmount-1*isc.Million-gasFee1) - // our L2 balance is 1 Mi - chain.AssertL2BaseTokens(userAgentID, 1*isc.Million) - // (the gas fee went to the chain's private account) - - // withdraw all base tokens back to L1 - req = solo.NewCallParams(accounts.Contract.Name, accounts.FuncWithdraw.Name). - WithAllowance(isc.NewAssetsBaseTokens(1 * isc.Million)) - - // estimate the gas fee and storage deposit - gas2, gasFee2, err := chain.EstimateGasOnLedger(req, userWallet, true) - require.NoError(t, err) - storageDeposit2 := chain.EstimateNeededStorageDeposit(req, userWallet) - - // send the withdraw request - req.WithGasBudget(gas2). - AddBaseTokens(gasFee2 + storageDeposit2). // including base tokens for gas fee and storage - AddAllowanceBaseTokens(storageDeposit2) // and withdrawing the storage as well - _, err = chain.PostRequestSync(req, userWallet) - require.NoError(t, err) - - // we are back to the initial situation, having been charged some gas fees - // in the process: - env.AssertL1BaseTokens(userAddress, utxodb.FundsFromFaucetAmount-gasFee1-gasFee2) - chain.AssertL2BaseTokens(userAgentID, 0) -} -``` - -The example above creates a chain and a wallet with `utxodb.FundsFromFaucetAmount` base tokens on L1. -Then, it sends 1 million tokens to the corresponding on-chain account by posting a -[`deposit`](../../../../references/iota-evm/core-contracts/accounts.md#deposit) request to the -[`accounts` core contract](../../../../references/iota-evm/core-contracts/accounts.md) on the chain. - -Finally, it sends a [`withdraw`](../../../../references/iota-evm/core-contracts/accounts.md#withdraw) request to the `accounts` core -contract to get the tokens back to L1. - -Both requests are affected by the gas fees and the storage deposit. -In some cases, it is possible to ignore these amounts if they are negligible compared to the transferred amounts. -In this case, however, we want to be very precise. - -### Deposit Requests - -#### 1. Request to Deposit Funds - -The first step in the deposit request is to create a request to deposit the funds with `solo.NewCallParams`. - -#### 2. Add Base Tokens - -In the example above we want to deposit 1 Mi, so we call `AddBaseTokens(1 * isc.Million)`. - -This instructs Solo to take that amount from the L1 balance and add it to the transaction. This is only possible for -on-ledger requests. - -#### 3. Calculate Gas Fees - -Once the chain executes the request, it will be charged a gas fee. - -We use `chain.EstimateGasOnLedger` before actually sending the request to estimate this fee. - -#### 4. Estimate Storage Deposit - -On-ledger requests also require a storage deposit. We use `EstimateNeededStorageDeposit` for this. As the 1 Mi already -included is enough for the storage deposit there’s no need to add more. - -#### 5. Add Gas Budget to the Request - -We adjust the request with the gas budget and the gas fee with `WithGasBudget` and `AddBaseTokens`, respectively. - -#### 6. Send the On-Ledger Request - -Finally, we send the on-ledger request with `PostRequestSync`. - -#### 7. The Chain Picks Up the Request - -Any attached base tokens (1 Mi + gas fee) are automatically credited to the sender's L2 account. - -#### 8. The chain executes the request - -The gas fee is deducted from the sender's L2 account. - -#### 9. The Transfer is Complete - -We have exactly 1 Mi on our L2 balance. - -### Withdraw Request - -The process for the `withdraw` request is similar to the [deposit process](#deposit-requests), with two main -differences: - -#### 1. Ensure the L1 Transaction Can Cover the Storage Deposit - -As the storage deposit is larger than the gas fee, we must ensure that the L1 transaction contains enough funds for the -storage deposit. These tokens are automatically deposited in our L2 account, and we immediately withdraw them. - -#### 2.Set the Request's Allowance - -We use `AddAllowanceBaseTokens` to set the _allowance_ of our request. The allowance specifies the maximum amount of -tokens the smart contract can debit from the sender's L2 account. - -It would fail if we posted the same `deposit` request from another user wallet (another private key). -Try it! Only the address owner can move those funds from the on-chain account. - -You can also try removing the `AddAllowanceBaseTokens` call. It will fail because a smart contract cannot deduct funds from the -sender's L2 balance unless explicitly authorized by the allowance. diff --git a/docs/content/developer/iota-evm/solo/how-tos/timelock.mdx b/docs/content/developer/iota-evm/solo/how-tos/timelock.mdx deleted file mode 100644 index a7fe5dba4b2..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/timelock.mdx +++ /dev/null @@ -1,124 +0,0 @@ ---- -description: You can post a time-locked request by using the Delay() method. You can mint NFTs by using the MintNFT() method. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - evm - - solo - - testing ---- - - -# Minting NFTs and Time Locks - -Let's examine some less commonly used member functions of the SoloContext. We will switch -to the `fairauction` example to show their usage. Here is the `startAuction()` function of -the `fairauction` test suite: - - - - - -```go -const ( - description = "Cool NFTs for sale!" - deposit = uint64(1000) - minBid = uint64(500) -) - -func startAuction(t *testing.T) (*wasmsolo.SoloContext, *wasmsolo.SoloAgent, wasmtypes.ScNftID) { - ctx := wasmsolo.NewSoloContext(t, fairauction.ScName, fairauction.OnDispatch) - auctioneer := ctx.NewSoloAgent() - nftID := ctx.MintNFT(auctioneer, []byte("NFT metadata")) - require.NoError(t, ctx.Err) - - // start the auction - sa := fairauction.ScFuncs.StartAuction(ctx.Sign(auctioneer)) - sa.Params.MinimumBid().SetValue(minBid) - sa.Params.Description().SetValue(description) - transfer := wasmlib.NewScTransferBaseTokens(deposit) // deposit, must be >=minimum*margin - transfer.AddNFT(&nftID) - sa.Func.Transfer(transfer).Post() - require.NoError(t, ctx.Err) - - return ctx, auctioneer, nftID -} -``` - - - - -The function first sets up the SoloContext as usual, and then it performs quite a bit of -extra work. This is because we want the startAuction() function to start an auction, so -that the tests that subsequently use startAuction() can then focus on testing all kinds of -bidding and auction results. - -First, we are going to need an agent that functions as the `auctioneer`. This auctioneer -will auction off an NFT. To provide the auctioneer with this NFT we use the `MintNFT()` -method to mint a fresh NFT into his account. The minting process will assign a unique NFT -ID. Of course, we check that no error occurred during the minting process. - -Now we are going to start the auction by calling the `startAuction` function of the -`fairauction` contract. We get the function descriptor in the usual way, but we also call -the `Sign()` method of the SoloContext to make sure that the transaction we are about to -post takes its assets from the auctioneer address, and the transaction will be signed with -the corresponding private key. Very often you won't care who posts a request, and we have -set it up in such a way that by default tokens come from the chain originator address, -which has been seeded with tokens just for this occasion. But whenever it is important -where the tokens come from, or who invokes the request, you need to specify the agent -involved by using the Sign() method. - -Next we set up the function parameters as usual. After the parameters have been set up, we -see something new happening. We create an `ScTransfer` proxy and initialize it with the -base tokens that we need to deposit, plus the freshly minted NFT that we are auctioning. -Next we use the `Transfer()` method to pass this proxy before posting the request. This is -exactly how we would do it from within the smart contract code. Note that the function -`NewScTransferBaseTokens()` is used as a shorthand to immediately initialize the new -`ScTransfer` proxy with the desired amount of base tokens. - -Finally, we make sure there was no error after posting the request and return the -SoloContext, `auctioneer` and `nftID`. That concludes the startAuction() function. - -Here is the first test function that uses our startAuction() function: - - - - - -```go -func TestStartAuction(t *testing.T) { - ctx, auctioneer, nftID := startAuction(t) - - nfts := ctx.Chain.L2NFTs(auctioneer.AgentID()) - require.Len(t, nfts, 0) - nfts = ctx.Chain.L2NFTs(ctx.Account().AgentID()) - require.Len(t, nfts, 1) - require.Equal(t, nftID, ctx.Cvt.ScNftID(&nfts[0])) - - // remove pending finalize_auction from backlog - ctx.AdvanceClockBy(61 * time.Minute) - require.True(t, ctx.WaitForPendingRequests(1)) -} -``` - - - - -This test function checks that the NFT was moved by `startAuction` from the auctioneer's -on-chain account to the chain's on-chain account. - -The `startAuction` function of the smart contract will also have posted a time-locked -request to the `finalizeAuction` function by using the `Delay()` method. Therefore, we -need to remove the pending `finalizeAuction` request from the backlog. - -First we move the logical clock forward to a point in time when that request is supposed -to have been triggered. Then we wait for this request to actually be processed. Note that -this will happen in a separate goroutine in the background, so we explicitly wait for the -request counters to catch up with the one request that is pending. - -The `WaitForPendingRequests()` method can also be used whenever a smart contract function -is known to [`post()`](../../schema/how-tos/post.mdx) a request to itself. Such requests are not immediately -executed, but added to the backlog, so you need to wait for these pending requests to -actually be processed. The advantage of having to explicitly wait for those requests is -that you can inspect the in-between state, which means that you can test even a function -that posts a request in isolation. diff --git a/docs/content/developer/iota-evm/solo/how-tos/view-sc.md b/docs/content/developer/iota-evm/solo/how-tos/view-sc.md deleted file mode 100644 index 60d34fa961f..00000000000 --- a/docs/content/developer/iota-evm/solo/how-tos/view-sc.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -description: Calling smart contract view functions with Solo. -image: /img/iota-evm/tutorial/call_view.png -tags: - - how-to - - evm - - solo - - testing ---- - -# Calling a View - -The following snippet shows how you can call the view function `getString` of the smart contract `solotutorial` without -parameters: - -```go -res, err := chain.CallView("example1", "getString") -``` - -The call returns a collection of key/value pairs `res` and an error result `err` in the typical Go fashion. - -[![Calling a view process](/img/iota-evm/tutorial/call_view.png)](/img/iota-evm/tutorial/call_view.png) - -The basic principle of calling a view is similar to sending a request to the smart contract. The essential difference is -that calling a view does not constitute an asynchronous transaction; it is just a direct synchronous call to the view -entry point exposed by the smart contract. - -Therefore, calling a view does not involve any token transfers. Sending a request (either on-ledger or off-ledger) to a -view entry point will result in an exception, returning all attached tokens to the sender minus fees (if any). - -Views are used to retrieve information about the smart contract's state, for example, to display on a website. Certain -Solo methods such as `chain.GetInfo`, `chain.GetGasFeePolicy`, and `chain.L2Assets` call views of -the [core smart contracts](../../../../references/iota-evm/core-contracts/overview.md) behind the scenes to retrieve the information -about the chain or a specific smart contract. - -## Decoding Results Returned by _PostRequestSync_ and _CallView_ - -The following is a specific technicality of the Go environment of _Solo_. - -The result returned by the call to an entry point from the Solo environment is an instance of -the [`dict.Dict`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/dict/dict.go) type, which is essentially a -map of `[]byte` key/value pairs, defined as: - -```go -type Dict map[kv.Key][]byte -``` - -`Dict` is also an implementation of -the [`kv.KVStore`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/kv.go) interface. The `kv` package and -subpackages provide many useful functions to work with the `Dict` type. - -:::note - -Both view and non-view entry points can produce results. -In normal operation, retrieving the result of an on-ledger request is impossible since it is an asynchronous operation. - -However, in the Solo environment, the call to `PostRequestSync` is synchronous, allowing the caller to inspect the -result. -This is a convenient difference between the mocked Solo environment and the distributed ledger used by Wasp nodes. -You can use it to make assertions about the results of a call in the tests. - -::: - -In the example above, `res` is a dictionary where keys and values are binary slices. The `getString` view returns the -value under the `"str"` key, and the value is a `string` encoded as a byte slice. The `codec` package provides functions -to encode/decode frequently used data types, including `string`. The following is a commonly used pattern to get a value -from the `Dict` and decode it: - -```go -var value string = codec.MustDecodeString(res["str"]) -``` diff --git a/docs/content/developer/iota-identity/contact.mdx b/docs/content/developer/iota-identity/contact.mdx deleted file mode 100644 index 6810781390c..00000000000 --- a/docs/content/developer/iota-identity/contact.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Contact -sidebar_label: Contact -description: Get in touch with the project maintainers. -image: /img/identity/icon.png -tags: - - reference - - identity ---- - -If you found a security related issue, please follow the [responsible disclosure policy](https://github.com/iotaledger/identity.rs/security/policy). - -For everything else, you can get in contact with the project by: - -- Creating an [issue](https://github.com/iotaledger/identity.rs/issues/new/choose) on [GitHub](https://github.com/iotaledger/identity.rs). -- Joining the `identity` channel on the [IOTA Discord](https://discord.iota.org/). diff --git a/docs/content/developer/iota-identity/contribute.mdx b/docs/content/developer/iota-identity/contribute.mdx deleted file mode 100644 index cc4a5aa62c5..00000000000 --- a/docs/content/developer/iota-identity/contribute.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Contribute to the project -sidebar_label: Contribute -description: Contribute to IOTA Identity by joining the Identity initiative, via the projects GitHub repository, documentation, or sharing your knowledge. -image: /img/identity/icon.png -tags: - - reference - - identity ---- - -**Thanks for thinking about contributing to the project! You can contribute using the following ways.** - -## Join the Identity Initiative - -The [Identity Initiative](https://github.com/iota-community/X-Team_IOTA_Identity) is a collaborative effort to help improve the developer experience that includes: - -- Quality assurance and review. -- Documentation. -- Code samples. - -If you would like to get involved, join the [#x-team-identity](https://discord.com/channels/397872799483428865/773274309861834782) channel on [Discord](https://discord.iota.org). - -## Contribute to the Project's GitHub Repository - -All of the code is open source and hosted on [GitHub](https://github.com/iotaledger/identity.rs) where you can: - -- [Report a bug](https://github.com/iotaledger/identity.rs/issues/new/choose). -- [Suggest a new feature](https://github.com/iotaledger/identity.rs/blob/main/.github/CONTRIBUTING.md). -- [Contribute to the documentation](#contribute-to-the-documentation). - -## Contribute to the Documentation - -This documentation is also open source and hosted on GitHub. - -If you want to contribute new documentation or fix an error, see the [contribution guidelines](https://github.com/iotaledger/iota-wiki/blob/main/.github/CONTRIBUTING.md). - -## Share Your Knowledge - -Helping others is an important part of any open source ecosystem. - -By sharing your knowledge with others, you can provide a lot of value to the community and maybe inspire someone else to learn and contribute. - -Take a look at what discussions are going on in the `#identity-discussion` channel on [Discord](https://discord.iota.org). - -Thanks :heart: diff --git a/docs/content/developer/iota-identity/explanations/about-alias-outputs.mdx b/docs/content/developer/iota-identity/explanations/about-alias-outputs.mdx deleted file mode 100644 index 1a91084ed84..00000000000 --- a/docs/content/developer/iota-identity/explanations/about-alias-outputs.mdx +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Alias Outputs -description: UTXO Alias Output -image: /img/identity/icon.png -tags: - - explanation - - identity - - did - - getting-started ---- - -# Alias Outputs - -:::info TL;DR - -The IOTA DID method uses Alias Outputs for storing DID Documents. -Alias Outputs are created via transactions, -and require a storage deposit to cover the data storage. -The deposit is refundable upon destruction of the output. - -Each Alias Output has an `Alias ID` that becomes the basis for the DID, -and which can be transferred through transactions to update DID Documents. - -::: - - -The IOTA DID method uses the IOTA ledger, which is based on the unspent transaction output (_UTXO_) model, -as well as the features of the Stardust upgrade, -which are fundamental to the IOTA DID method. - -The Alias Output is used to store a DID Document on the ledger. -It is a specific implementation of the UTXO _state machine_ that can hold arbitrary data in its `State Metadata`. -The Alias Output has two kinds of controllers, a state controller and a governor. - -The state controller can only execute state transitions, which update the data in the `State Metadata`. - -The governor, on the contrary, can't update the `State Metadata` but can change controllers and even destroy the Alias Output. - -A controller can be either Ed25519 Address, Alias Address or an _NFT_ Address. Only one of each of these types can be set for an Alias Output. - -To create a new Alias Output, a transaction must be made that includes another Output as input, -a Basic Output, for example, and the new Alias Output as output. - -### Storage Deposit - -The arbitrary data stored in the `State Metadata` of the Alias output must be covered by a -storage deposit. -This helps to control the ledger size from growing uncontrollably while guaranteeing the data -is indefinitely stored on the ledger, which is important for resolving DID Documents. -This deposit is fully refundable and can be reclaimed when the output is destroyed. - -Both the state controller and the governor can control the tokens stored in the Alias Output. -_Nodes_ expose an API to calculate the required deposit depending on the size of the data stored. - -### Alias ID - -Each Alias Output has an `Alias ID`. This ID is assigned after a transaction creates the Alias Output. -The actual DID is derived from this `Alias ID`, so it will be unknown before publishing the transaction. -Consequently, the DID inside the `State Metadata` is replaced by the placeholder `did:0:0` to indicate self. - -If a transaction has an Alias Output as input, its `Alias ID` can be kept by one of its outputs. -This feature is necessary for updating the DID Documents since the DID itself is derived from the `Alias ID`. diff --git a/docs/content/developer/iota-identity/explanations/decentralized-identifiers.mdx b/docs/content/developer/iota-identity/explanations/decentralized-identifiers.mdx deleted file mode 100644 index a892b851746..00000000000 --- a/docs/content/developer/iota-identity/explanations/decentralized-identifiers.mdx +++ /dev/null @@ -1,144 +0,0 @@ ---- -description: The Decentralized Identifiers (DID) standard from W3C is the fundamental standard that supports the concept of a decentralized digital identity. Explore the basic aspects of the DID standard. -image: /img/identity/icon.png -tags: - - explanation - - identity - - did - - getting-started - - verification-method - - verifiable-credentials ---- - -# Decentralized Identifiers (DID) - -:::info TL;DR - -DIDs are unique identifiers that can be resolved to DID Documents containing public keys and URIs for identity verification and public information. - -Adhering to the W3C's DID specifications, IOTA's implementation ensures interoperability and security within its ledger. - -DIDs support self-sovereign identity, allowing identity owners to control their creation and destruction, -while facilitating encrypted communication. - -::: - -A DID is a unique identifier that can be resolved to a DID Document. This document contains data such as public keys, enabling the holder to prove ownership over their personal data, but also URIs that link to public information about the identity. DIDs are the fundamental building blocks of decentralized digital identity. -This implementation complies with the [DID specifications v1.0](https://www.w3.org/TR/did-core//) from the World Wide Web Consortium (W3C). - -In the IOTA Identity framework, we have implemented the DID standard according to the `iota` [DID Method Specification](../../../references/iota-identity/iota-did-method-spec.mdx). Other implementations of DID on IOTA must follow the `iota` DID Method Specification if they want to use the `iota` method name. Libraries implementing the `iota` DID Method Specification are provided for [Rust](../getting-started/rust.mdx) and [WASM](../getting-started/wasm.mdx). - -An example of a DID conforming to the `iota` method specification: -`did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe` - -## Decentralized Identifiers - -A Decentralized Identifier, or DID, is a unique identifier that is tied to a subject. This subject can be anything, like a person, an organization, an IoT device, or even an object. The identifier can be used by the subject to identify themselves through a digital format, providing a basis for online identification. The identifier looks like a set of random characters that includes some prefixes to determine which standard and implementation is used: - -`did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe` - -The World Wide Web Consortium (W3C) is a well-known standardization body that has standardized how DIDs should look and work. -This provides a basis for different technologies that implement the [DID standard](https://www.w3.org/TR/did-spec-registries/#did-methods) to achieve interoperability. -Keep in mind that, unfortunately, most of these methods are outdated and not maintained. - -## DID Documents - -The purpose of a DID is to help navigate to a DID Document, -which is a document containing more information regarding the identity subject. -This document contains data such as public keys, enabling the subject to prove ownership over their personal data, -but can contain additional information on how to interact with the subject. - -The identifier contains all information to resolve a DID, providing the latest DID Document. -The first three characters `did` indicate that the DID standard from W3C must be used to resolve the identifier. -It is followed by a unique method name, in our case `iota`, to indicate that the IOTA method is used. - -The IOTA method is a specific implementation following the [IOTA DID Method Specification](../../../references/iota-identity/iota-did-method-spec.mdx). -This provides unique rules for the protocol to follow in order to manage a DID Document. -In our case, it describes how DID Documents are uploaded and queried to and from the IOTA ledger. - -Lastly, a DID also contains a set of random characters that are unique per identity and resolve to a single DID Document. - -:::tip Requires basic knowledge of Asymmetric Encryption - -The following sections require some basic knowledge of Asymmetric Encryption. -Please read or view some materials on the subject before continuing. - -::: - -### DID Document Anatomy - -A DID Document mostly contains two important pieces of data: verification methods and services. - -#### Verification Methods - -Verification methods contain public key material that can be used to prove ownership over the identity, -by cryptographically signing something with the associated private key. -The public key can be used to verify that the identity subject signed the data and therefore controls the private key. -Ownership over the private keys, therefore, proves ownership over the identity. - -This also means that it is very important to keep the private keys safe and secure. -In addition, the public keys allow users to send encrypted data to the identity that only the identity owner can decrypt. - -:::caution - -Never share your private keys, seeds, passphrases with anyone. Not even IOTA Foundation members. -This may lead to loss of funds or control over your own digital identity. - -::: - -#### Services - -Services are URIs that point to more information about the identity. -This could be something as simple as a website for an organizational identity. -These services are publicly available for all to read, -and should not contain Personal Identifiable Information (PII) in the case of human identities. - -## Why Use DIDs? - -DIDs allow any subject to have a unique identifier that they can prove ownership of, -and at the same time provide a way to send them encrypted messages. -The Identity is Self-Sovereign, meaning the subject is always in control; -whether it is [creating](../how-tos/decentralized-identifiers/create.mdx), [updating](../how-tos/decentralized-identifiers/update.mdx), or [destroying](../how-tos/decentralized-identifiers/delete.mdx) it. - -### Verifiable Credentials - -DIDs become more interesting when you combine them with [verifiable credentials (VC)](verifiable-credentials.mdx). -In essence, verifiable credentials (VCs) are signed statements by trusted third parties about a certain identity. -The signer, or Issuer, is referenced by the DID and so is the subject, often called the holder. -The holder controls a copy of this statement and can share it with other parties, the _Verifiers_, -who can verify the statement and check which party made the statement without asking the Issuer. -Instead, they can verify the issuer's signature by checking the issuer's DID Document. - -This puts Holders back in control over their own data, -while making the data much more trustworthy as it has become verifiable. - -## Why Use Iota Identity Over Other Implementations? - -IOTA Identity is a framework to implement Self-Sovereign Identities on IOTA. -Inherently, IOTA provides some unique features that have a major impact on the usability of the framework. - -### Availability and Accessibility - -DID Documents are stored in the ledger state and are covered storage deposit. -This guarantees that all nodes will have an up-to-date copy of the latest DID Document. -Resolving a DID into its document can usually be done by any IOTA node in the network. -This solves many issues regarding availability, accessibility, and synchronization. - -### Layer1 Interactions - -DID Documents are stored in [Alias Outputs](./about-alias-outputs.mdx), -this allows them to directly interact with Layer 1 artifacts like NFTs and native assets. - -For instance, an Alias Output representing a DID can hold native assets or control NFTs. -Transferring funds between DIDs is also possible on Layer 1. - -### Ease-of-use - -IOTA Identity abstracts the details of the DID standard by providing easy-to-use APIs that allow standardized behavior. -It also allows more flexible and complex management of DID Documents. - -### Secure Storage - -IOTA Identity provides a -Stronghold solution -for managing secrets securely, without requiring developers to reinvent the security wheel. diff --git a/docs/content/developer/iota-identity/explanations/verifiable-credentials.mdx b/docs/content/developer/iota-identity/explanations/verifiable-credentials.mdx deleted file mode 100644 index a1284c8f45f..00000000000 --- a/docs/content/developer/iota-identity/explanations/verifiable-credentials.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -description: Verifiable credentials are statements about the holder. They can be verified online or in person, and the holder decides who to share them with. -image: /img/identity/icon.png -tags: - - explanation - - identity - - verifiable-credentials ---- -# Verifiable Credentials - -:::info TL;DR - -Verifiable credentials (VCs) are digital statements that can be cryptographically proven, like a digital passport, -and are used within systems to assert certain properties or capabilities of an entity. - -In the IOTA Identity framework, this is managed with decentralized identifiers (DIDs) on the Tangle. -Subjects and issuers use their DIDs for the creation and verification of credentials. - -::: - -Credentials are statements about an entity, -such as properties that the entity possesses or capabilities that they have, -like a driver's license, passports, or a person's age. -Verifiable credentials (VCs) are statements (e.g., Alice has a driver's license) -that can be cryptographically verified by a third party, either online or in person. -Additionally, the holder of the VC decides what is shared and who it is shared with. - -There are several types of actors that play different roles in a Verifiable Credential system. -We'll start with a common example of how things work in the world today using physical credentials and centralized databases, -and outline the roles that various entities play in the Verifiable Credential system. - -## Example - Passport Issuance - -A government (the _issuer_) issues a passport asserting citizenship (the _Verifiable Credential_) to Alice (the _subject_ and _Holder_), -and writes the information to a database (the _Verifiable Data Registry_). -When crossing the border, Alice (the _Holder_) presents her passport to a border agent (the _Verifier_), -who can verify that Alice (the _subject_) is indeed a citizen. - - - -**Subject:** An entity about which claims are made – in this example, that Alice (the _subject_) is a citizen of this country. - -**Holder:** Any entity with a verifiable credential – Alice (the _Holder_) possesses the passport (the _VC_). - -**Issuer:** An entity which asserts claims about a subject – The governing body (the _issuer_), which is trusted, issues Alice a passport. - -**Verifier:** An entity which checks if the VC a holder presents is legitimate – The border agent (the _Verifier_) trusts the government (the _issuer_) which issued Alice her passport and validates that Alice (the _subject_) is a citizen. - -:::note - -See the [Verifiable Credentials Data Model 1.0 Specification](https://w3c.github.io/vc-data-model/) for more information. - -::: - -## Verifiable Credentials in IOTA - -In the IOTA Identity framework, instead of a physical passport being given to Alice and its information written -into a centralized database owned by the government, Alice receives a digital verifiable credential, -and the information required for verification in the future is written to the Tangle. - -At a high level, the creation and verification of a VC on IOTA works as follows: - - -The first step is to create a verifiable credential that requires the subject (Alice) and issuer (the government) to -have [DIDs](./decentralized-identifiers.mdx) published on the Tangle and a set of statements being asserted (that Alice has a passport). -The issuer signs the credential with their private key and publishes the public key to the Tangle. - -Once the issuer is confident that the credential satisfies its expectations, -the credential is stored and transmitted to the subject in a secure manner (off-chain). - -Validation is performed by looking up the issuer's public key on the Tangle, -the holder proving ownership of their DID to the verifier (evidence), -and validating that the issuing party has indeed signed the credential. - diff --git a/docs/content/developer/iota-identity/explanations/verifiable-presentations.mdx b/docs/content/developer/iota-identity/explanations/verifiable-presentations.mdx deleted file mode 100644 index 1718528c8e1..00000000000 --- a/docs/content/developer/iota-identity/explanations/verifiable-presentations.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Verifiable presentations are a way for a holder to present one or more verifiable credentials to a verifier. -image: /img/identity/icon.png -tags: - - explanation - - identity ---- - -# Verifiable Presentations - -A verifiable presentation is the recommended data format for sharing one or more [verifiable credentials](./verifiable-credentials.mdx). -It is constructed and signed by a holder to prove control over their credentials and can be presented to a verifier for validation. - -For instance, after an issuer [creates and issues](./../how-tos/verifiable-credentials/create.mdx) a [verifiable credential](./verifiable-credentials.mdx) to a holder, such as a university issuing a degree to a graduate, -the holder stores it securely until asked to present it. -A company could then request proof of that university degree: the holder can [create a verifiable presentation](./../how-tos/verifiable-credentials/create.mdx) -containing their credential, already signed by their university, and present it to the company to [validate](./../how-tos/verifiable-credentials/create.mdx#validate-a-vc). - -Note that verifiable presentations that contain personal data should, as with verifiable credentials, be transmitted and stored securely off-chain to satisfy data privacy regulations such as [GDPR](https://gdpr.eu/). - -:::note - -See the [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/#presentations) for more information on verifiable presentations. - -::: - -## Security Considerations - -### Replay Attacks - -A malicious actor could potentially store a verifiable presentation without a challenge -and replayed to a different verifier, impersonating the holder. -This is because the holder's signature on a presentation would still be seen as valid indefinitely, -until they [rotate](https://www.w3.org/TR/did-core/#verification-method-rotation) the verification method used. - -To mitigate this, verifiers should always send a unique challenge when requesting a verifiable presentation. -This challenge can be set as the `nonce` property of the JWS by the holder during signing. -The digital signature prevents these properties from being altered as it would invalidate the signature, effectively preventing a malicious -actor from injecting different values into old verifiable presentations. A presentation without a challenge in its proof that matches what was -sent by the verifier should be considered invalid. - -The challenge string should be sufficiently random and unique for each verifiable presentation requested by a verifier to avoid -being predicted. - -Holders may additionally specify that their signature on a verifiable presentation expires after a short duration, as -per `JwtPresentationOptions`. However, verifiers and different implementations could ignore that property, -so setting a signature expiration alone should not be relied upon. diff --git a/docs/content/developer/iota-identity/faq.mdx b/docs/content/developer/iota-identity/faq.mdx deleted file mode 100644 index ce82b2d9544..00000000000 --- a/docs/content/developer/iota-identity/faq.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Frequently Asked Questions -sidebar_label: FAQ -description: Frequently Asked Question regarding IOTA Identity. -image: /img/identity/icon.png -tags: - - reference - - identity ---- - -This page contains frequently asked questions regarding the Identity Library and Self-Sovereign Identity in general. - -### What programming languages are supported by the IOTA Identity framework? - -We currently provide a Rust library and a JavaScript library for both the browser and Node.js via WebAssembly (Wasm) bindings. See the "Programming Languages" section for more information. - -### Do I need to have IOTA tokens to start building with IOTA Identity? - -You need IOTA tokens to create identities, in order to pay the storage deposit. - -### How do I prove control over my DID? - -Control over an identity is ultimately tied to the control over cryptographic key material (something you have). - -### How do I store my private keys? - -Theoretically, you can store the keys however you like. Where possible, we provide a secure default using IOTA Stronghold, a secure software implementation for isolating digital secrets with encrypted storage. For even better guarantees, you could look into hardware-based key storage. - -### Do I need a Permanode to use IOTA Identity? - -You can get started without one, but currently, you require access to a Permanode (a node that stores the entire history of the Tangle) to reliably resolve the history of identities. - -### Can I use IOTA Identity on Android or iOS? - -We currently do not supply dedicated bindings for Kotlin or Swift. There has been some success running the Wasm bindings on mobile, however. - -### Can I use IOTA Identity on embedded devices? - -We currently do not supply dedicated bindings catering to embedded devices with restricted capabilities. You can try to compile the Rust library for your target platform or use a gateway in front of the devices to handle IOTA Identity interactions. - -### What should I do if my private key is compromised? - -If you still have control over your identity, rotate the key material ASAP! If an attacker has locked you out of your identity, there is not much you can do. Notify contacts that your identity has been compromised and start fresh with a new one. For this reason, we suggest using different keys for day-to-day signing and authentication operations and instead storing private keys capable of updating your DID Document securely and separately. - -### Are verifiable credentials stored on the Tangle? - -Verifiable credentials, particularly those with personal identifiable information, are supposed to be stored securely off-Tangle on user devices or systems. As a user, you are in charge of storing your credentials securely and sharing them with other parties on a need-to-know basis. - -### Do I need to hide my DID? Will people be able to identify me by my DID? - -A DID Document should not contain any information linking back to you as a person. However, there is the chance of entities correlating information about you from your DID if used across multiple issuers and verifiers. To minimize this risk, it is advisable to use different DIDs for different use cases. diff --git a/docs/content/developer/iota-identity/getting-started/rust.mdx b/docs/content/developer/iota-identity/getting-started/rust.mdx deleted file mode 100644 index a0e23c9a759..00000000000 --- a/docs/content/developer/iota-identity/getting-started/rust.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_label: Rust -description: Getting started with the IOTA Identity Rust Library. -image: /img/identity/icon.png -tags: - - identity - - rust - - setup - - install - - getting-started ---- -# Getting Started with Rust - -## Requirements - -- [Rust](https://www.rust-lang.org/) (>= 1.62) -- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) - -## Include the Library - -To include IOTA Identity in your project, add it as a dependency in your `Cargo.toml`: - -### Latest Stable Release - -This version is published to crates.io and is **stable**, following semantic versioning. - -```toml -[dependencies] -identity_iota = { version = "1.2.0" } -``` - -### Development Release - -This version matches the `main` branch of this repository. It has all the **latest features**, but as such, it **may also have undocumented breaking changes**. - -```toml -[dependencies] -identity_iota = { git = "https://github.com/iotaledger/identity.rs", branch = "main"} -``` - -## Examples - -To try out the [examples](https://github.com/iotaledger/identity.rs/tree/v1.2.0/examples), you should: - -1. Clone the repository: - -```bash -git clone https://github.com/iotaledger/identity.rs -``` - -2. Build the repository: - -```bash -cargo build -``` - -3. Run your first example: - -```bash -cargo run --example 0_create_did -``` - -## API Reference - -You can find the API reference for the Rust library on [docs.rs](https://docs.rs/identity_iota/latest/identity_iota/index.html). - -If you would like to build the documentation, locally you can do so with the following command: - -``` -RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc -p identity_iota --all-features --no-deps --open -``` diff --git a/docs/content/developer/iota-identity/getting-started/wasm.mdx b/docs/content/developer/iota-identity/getting-started/wasm.mdx deleted file mode 100644 index 4ad780886ae..00000000000 --- a/docs/content/developer/iota-identity/getting-started/wasm.mdx +++ /dev/null @@ -1,336 +0,0 @@ ---- -sidebar_label: Wasm -description: Getting started with the IOTA Identity WASM Library. -image: /img/identity/icon.png -tags: - - identity - - wasm - - setup - - install - - getting-started ---- - -# Getting Started with Wasm - -## Minimum Requirements - -- [Node.js](https://nodejs.org/en) (>= `v16`) - -## Install the Library - -You can install the latest stable version of the library by running the following command: - -```bash npm2yarn -npm install @iota/identity-wasm -``` - -## Build the Library - -Alternatively, you can build the bindings yourself if you have Rust installed. -If not, refer to [rustup.rs](https://rustup.rs) for the installation. - -### Requirements - -- [Node.js](https://nodejs.org/en) (>= `v16`) -- [Rust](https://www.rust-lang.org/) (>= 1.62) -- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) - -### 1. Install `wasm-bindgen-cli` - -If you want to build the library from source, -you will first need to manually install [`wasm-bindgen-cli`](https://github.com/rustwasm/wasm-bindgen). -A manual installation is required because we use the [Weak References](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html) feature, -which [`wasm-pack` does not expose](https://github.com/rustwasm/wasm-pack/issues/930). - -```bash -cargo install --force wasm-bindgen-cli -``` - -### 2. Install Dependencies - -After installing `wasm-bindgen-cli`, you can install the necessary dependencies using the following command: - -```bash npm2yarn -npm install -``` - -### 3. Build - - - - -You can build the bindings for `node.js` using the following command: - -```bash npm2yarn -npm run build:nodejs -``` - - - - -You can build the bindings for the `web` using the following command: - -```bash npm2yarn -npm run build:web -``` - - - - -## NodeJS Usage - -The following code creates a new IOTA DID Document suitable for publishing to a local test network, like the -IOTA Sandbox. - - - - -```typescript -const { - KeyPair, - KeyType, - MethodScope, - IotaDocument, - IotaVerificationMethod, - IotaService, - MethodRelationship, - IotaIdentityClient, -} = require('@iota/identity-wasm/node'); -const { Client } = require('@iota/client-wasm/node'); - -// The endpoint of the IOTA node to use. -const API_ENDPOINT = 'http://127.0.0.1:14265'; - -/** Demonstrate how to create a DID Document. */ -async function main() { - // Create a new client with the given network endpoint. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - const didClient = new IotaIdentityClient(client); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp = await didClient.getNetworkHrp(); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - - // Insert a new Ed25519 verification method in the DID document. - let keypair = new KeyPair(KeyType.Ed25519); - let method = new IotaVerificationMethod( - document.id(), - keypair.type(), - keypair.public(), - '#key-1', - ); - document.insertMethod(method, MethodScope.VerificationMethod()); - - // Attach a new method relationship to the existing method. - document.attachMethodRelationship( - document.id().join('#key-1'), - MethodRelationship.Authentication, - ); - - // Add a new Service. - const service = new IotaService({ - id: document.id().join('#linked-domain'), - type: 'LinkedDomains', - serviceEndpoint: 'https://iota.org/', - }); - document.insertService(service); - - console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); -} - -main(); -``` - -### Expected Output - -``` -Created document { - "doc": { - "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000", - "verificationMethod": [ - { - "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", - "controller": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "z4SxypezRxr1YdMAJBePfHGxZ9hNZ53WVixZq3PbUcztW" - } - ], - "authentication": [ - "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" - ], - "service": [ - { - "id": "did:iota:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - } - ] - }, - "meta": { - "created": "2022-09-09T11:29:32Z", - "updated": "2022-09-09T11:29:32Z" - } -} -``` - -## Web Usage - -### Set Up - -The library loads the WASM file with an HTTP GET request, so you must copy the `.wasm` file the root of the `dist` folder. - -#### Rollup - -- Install `rollup-plugin-copy`: - -```bash npm2yarn -npm install rollup-plugin-copy --save-dev -``` - -- Add the copy plugin usage to the `plugins` array under `rollup.config.js`: - -```js -// Include the copy plugin -import copy from 'rollup-plugin-copy'; - -// Add the copy plugin to the `plugins` array of your rollup config: -copy({ - targets: [ - { - src: 'node_modules/@iota/client-wasm/web/wasm/client_wasm_bg.wasm', - dest: 'public', - rename: 'client_wasm_bg.wasm', - }, - { - src: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', - dest: 'public', - rename: 'identity_wasm_bg.wasm', - }, - ], -}); -``` - -#### Webpack - -- Install `copy-webpack-plugin`: - -```bash npm2yarn -npm install copy-webpack-plugin --save-dev -``` - -```js -// Include the copy plugin -const CopyWebPlugin= require('copy-webpack-plugin'); - -// Add the copy plugin to the `plugins` array of your webpack config: - -new CopyWebPlugin({ - patterns: [ - { - from: 'node_modules/@iota/client-wasm/web/wasm/client_wasm_bg.wasm', - to: 'client_wasm_bg.wasm' - }, - { - from: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', - to: 'identity_wasm_bg.wasm' - } - ] -}), -``` - -### Web Usage Example - -```typescript -import * as client from '@iota/client-wasm/web'; -import * as identity from '@iota/identity-wasm/web'; - -/** Demonstrate how to create a DID Document. */ -async function createDocument() { - // Create a new client with the given network endpoint. - const iotaClient = new client.Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - const didClient = new identity.IotaIdentityClient(iotaClient); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp = await didClient.getNetworkHrp(); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new identity.IotaDocument(networkHrp); - - // Insert a new Ed25519 verification method in the DID document. - let keypair = new identity.KeyPair(identity.KeyType.Ed25519); - let method = new identity.IotaVerificationMethod( - document.id(), - keypair.type(), - keypair.public(), - '#key-1', - ); - document.insertMethod(method, identity.MethodScope.VerificationMethod()); - - // Attach a new method relationship to the existing method. - document.attachMethodRelationship( - document.id().join('#key-1'), - identity.MethodRelationship.Authentication, - ); - - // Add a new Service. - const service = new identity.IotaService({ - id: document.id().join('#linked-domain'), - type: 'LinkedDomains', - serviceEndpoint: 'https://iota.org/', - }); - document.insertService(service); - - console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); -} - -client - .init() - .then(() => identity.init()) - .then(() => { - await createDocument(); - }); - -// or - -(async () => { - await client.init(); - await identity.init(); - - await createDocument(); -})(); - -// Default path is "identity_wasm_bg.wasm", but you can override it like this -await identity.init('./static/identity_wasm_bg.wasm'); -``` - -You need to call `identity.init().then()`, -or `await identity.init()` to load the Wasm file from the server if not available, -because of that **it will only be slow for the first time**. - -## Examples in the Wild - -You may find it useful to see how the WASM bindings are being used in existing applications: - -- [Zebra IOTA Edge SDK](https://github.com/ZebraDevs/Zebra-Iota-Edge-SDK) (mobile apps using Capacitor.js + Svelte) - -## [API Reference](../../../references/iota-identity/wasm/api_ref.md) - -## [Examples](https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/README.md) diff --git a/docs/content/developer/iota-identity/glossary.mdx b/docs/content/developer/iota-identity/glossary.mdx deleted file mode 100644 index a057268845b..00000000000 --- a/docs/content/developer/iota-identity/glossary.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -description: Glossary for IOTA Identity, defines the terms used in this specification and throughout decentralized identifier infrastructure. -image: /img/identity/icon.png -tags: - - reference - - identity - - verification-method ---- - -# Glossary - -This section defines the terms used in this specification, [sourced from W3](https://www.w3.org/TR/did-core/#terminology), and throughout decentralized identifier infrastructure. A link to these terms is included whenever they appear in this specification. - -The first part of the glossary describes the terminology by the W3C. The [second describes](#iota-terminology) the terminology for IOTA-related topics. - -## W3C Terminology - -### Authentication - -A process (typically some type of protocol) by which an entity can prove it has a specific attribute or controls a specific secret using one or more verification methods. With DIDs, a common example would be proving control of the private key associated with a public key published in a DID document. - -### Decentralized Identifier (DID) - -A globally unique persistent identifier that does not require a centralized registration authority because it is generated or registered cryptographically. The generic format of a DID is defined in the DID Core specification. A specific DID scheme is defined in a DID method specification. Many, but not all, DID methods make use of distributed ledger technology (DLT) or some other form of decentralized network. - -### Decentralized Identity Management - -A type of identity management that is based on the use of decentralized identifiers. Decentralized identity management extends authority for identifier generation, registration, and assignment beyond traditional roots of trust, such as X.500 directory services, the Domain Name System, and most national ID systems. - -### DID Controller - -An entity that can make changes to a DID document. A DID may have more than one DID controller. The DID controller(s) can be denoted by the optional controller property at the top level of the DID document. Note that one DID controller may be the DID subject. - -### DID Delegate - -An entity to whom a DID controller has granted permission to use a verification method associated with a DID via a DID document. For example, a parent who controls a child's DID document might permit the child to use their personal device for authentication purposes. In this case, the child is the DID delegate. The child's personal device would contain the private cryptographic material enabling the child to authenticate using the DID. However, the child may not be permitted to add other personal devices without the parent's permission. - -### DID Document - -A set of data describing the DID subject, including mechanisms, such as public keys and pseudonymous biometrics, that the DID subject or a DID delegate can use to authenticate itself and prove its association with the DID. A DID document may also contain other attributes or claims describing the DID subject. A DID document may have one or more different representations as defined in [§ 6. Representations](https://www.w3.org/TR/did-core/#representations) or in the W3C DID Specification Registries [DID-SPEC-REGISTRIES](https://www.w3.org/TR/did-core/#bib-did-spec-registries). - -### DID Fragment - -The portion of a DID URL that follows the first hash sign character (#). DID fragment syntax is identical to URI fragment syntax. - -### DID Method - -A definition of how a specific DID scheme must be implemented to work with a specific verifiable data registry. A DID method is defined by a DID method specification, which must specify the precise operations by which DIDs are created, resolved, and deactivated, where DID documents are written and updated. [See W3's Methods](https://www.w3.org/TR/did-core/#methods). - -### DID Path - -The portion of a DID URL that begins with and includes the first forward-slash (/) character and ends with either a question mark (?) character or a fragment hash sign (#) character (or the end of the DID URL). DID path syntax is identical to URI path syntax. See [Path](https://www.w3.org/TR/did-core/#path). - -### DID Query - -The portion of a DID URL that follows and includes the first question mark character (?). DID query syntax is identical to URI query syntax. See [Query](https://www.w3.org/TR/did-core/#query). - -### DID Resolution - -The function that takes, as an input, a DID and a set of input metadata and returns a DID document in a conforming representation plus additional metadata. This function relies on the "Read" operation of the applicable DID method. The inputs and outputs of this function are defined in [Resolution](https://www.w3.org/TR/did-core/#resolution). - -### DID Resolver - -A DID resolver is a software or hardware component that performs the DID resolution function by taking a DID as input and producing a conforming DID document as output. - -### DID Scheme - -The formal syntax of a decentralized identifier. The generic DID scheme begins with the prefix "did:" as defined in the section of the DID Core specification. Each DID method specification must define a specific DID scheme that works with that particular DID method. In a specific DID method scheme, the DID method name must follow the first colon and terminate with the second colon, such as "did:example:". - -### DID Subject - -The entity identified by a DID and described by a DID document. A DID has exactly one DID subject. Anything can be a DID subject: a person, group, organization, physical thing, digital thing, logical thing, and so on. - -### DID URL - -A DID plus any additional syntactic component that conforms to the definition in § 3.2 DID URL Syntax. This includes an optional DID path, optional DID query (and its leading ? character), and optional DID fragment (and its leading # character). - -### DID URL Dereferencing - -The function that takes as its input a DID URL, a DID document, plus a set of dereferencing options, and returns a resource. This resource may be a DID document plus additional metadata, or it may be a secondary resource contained within the DID document, or it may be a resource entirely external to the DID document. If the function begins with a DID URL, it uses the DID resolution function to fetch a DID document indicated by the DID contained within the DID URL. The dereferencing function can then perform additional processing on the DID document to return the dereferenced resource indicated by the DID URL. The inputs and outputs of this function are defined in [DID URL Dereferencing](https://www.w3.org/TR/did-core/#did-url-dereferencing). - -## IOTA Terminology - -### Distributed Ledger (DLT) - -A distributed database in which the various nodes use a consensus protocol to maintain a shared ledger in which each transaction is cryptographically signed and chained to the previous transaction. - -### Public Key Description - -A data object contained inside a DID document that contains all the metadata necessary to use a public key or verification key. - -### Resource - -As defined by [RFC3986](https://www.rfc-editor.org/rfc/rfc3986): "...the term 'resource' is used in a general sense for whatever might be identified by a URI." Similarly, any resource may serve as a DID subject identified by a DID - -### Representation - -As defined for HTTP by [RFC7231](https://httpwg.org/specs/rfc7231.html): "information that is intended to reflect a past, current, or desired state of a given resource, in a format that can be readily communicated via the protocol, and that consists of a set of representation metadata and a potentially unbounded stream of representation data." A DID document is a representation of information describing a DID subject. The [Representations](https://www.w3.org/TR/did-core/#representations) section of the DID Core specification defines several representation formats for a DID document. - -### Service - -A means of communicating or interacting with the DID subject or associated entities via one or more service endpoints. Examples include discovery services, agent services, social networking services, file storage services, and verifiable credential repository services. - -### Service Endpoint - -A network address (such as an HTTP URL) at which a service operates on behalf of a DID subject. - -### Uniform Resource Identifier (URI) - -The standard identifier format for all resources on the World Wide Web as defined by [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). A DID is a type of URI scheme. - -### Verifiable Credential - -A standard data model and representation format for cryptographically-verifiable digital credentials as defined by the W3C [VC-DATA-MODEL](https://www.w3.org/TR/vc-data-model/). - -### Verifiable Data Registry - -A system that facilitates the creation, verification, updating, or deactivation of decentralized identifiers and DID documents. A verifiable data registry may also be used for other cryptographically-verifiable data structures, such as verifiable credentials. For more information, see [VC-DATA-MODEL](https://www.w3.org/TR/vc-data-model/). - -### Verifiable Timestamp - -A verifiable timestamp enables a third-party to verify that a data object existed at a specific moment in time and that it has not been modified or corrupted since that moment in time. If the data integrity were to be reasonably modified or corrupted since that moment in time, the timestamp is not verifiable. - -### Verification Method - -A set of parameters that can be used together with a process or protocol to independently verify a proof. For example, a public key can be used as a verification method with respect to a digital signature; in such usage, it verifies that the signer possessed the associated private key. - -"Verification" and "proof" in this definition are intended to apply broadly. For example, a public key might be used during Diffie-Hellman key exchange to negotiate a shared symmetric key for encryption. This guarantees the integrity of the key agreement process. It is thus another type of verification method, even though descriptions of the process might not use the words "verification" or "proof." - -### Verification Relationship - -An expression of the relationship between the DID subject and a verification method. An example of a verification relationship is [authentication](https://www.w3.org/TR/did-core/#authentication). - -### Universally Unique Identifier (UUID) - -A type of globally unique identifier defined by [RFC4122](https://www.rfc-editor.org/rfc/rfc4122). UUIDs are similar to DIDs in that they do not require a centralized registration authority. UUIDs differ from DIDs in that they are not resolvable or cryptographically verifiable. -In addition to the terminology above, this specification also uses terminology from the [INFRA](https://infra.spec.whatwg.org/) specification to formally define the abstract data model. When [INFRA](https://infra.spec.whatwg.org/) terminology is used, such as string, ordered set, and map, it is linked directly to that specification. -In addition to the terminology above, this specification also uses terminology from the [INFRA] specification to formally define the abstract data model. When [INFRA] terminology is used, such as string, ordered set, and map, it is linked directly to that specification. diff --git a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/create.mdx b/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/create.mdx deleted file mode 100644 index 2ad9b706031..00000000000 --- a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/create.mdx +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: Creating a Decentralized Identity -sidebar_label: Create and Publish -description: Create DID Documents and publish them to the Tangle -image: /img/identity/icon.png -tags: - - how-to - - identity - - did ---- - - -If you want to benefit from Self-Sovereign Identity, -you need to create a [Decentralized Identity](../../explanations/decentralized-identifiers.mdx). -This identity consists of many parts that have different functions. - -:::note DID method Specification - -Note that the Iota Identity Framework follows [IOTA DID Method Specification](../../../../references/iota-identity/iota-did-method-spec.mdx). - -::: - -## Identity Generation Process - -### 1. Get Funds to Cover the Storage Deposit - -The first thing you will need to generate an identity is an address with enough funds to cover -the [Storage Deposit](../../explanations/about-alias-outputs.mdx#storage-deposit). -In test networks, you can use a [faucet](https://faucet.testnet.shimmer.network/) to request funds. - -:::tip - -If you want to use the main Shimmer or IOTA networks, -you will need an output with actual Shimmer or IOTA funds to create a new Alias Output that represents a DID. - -::: - -
- - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/0_create_did.rs#L52 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/0_create_did.ts#L40-L51 -``` - - - -
- -### 2. Create the Content for the DID Document - -As a bare minimum, a [DID document](../../explanations/decentralized-identifiers.mdx) needs at least one verification method. - -At this point, the DID itself is unknown since the Alias Output is not published yet and didn't get an `Alias ID` assigned. - -:::tip - -You can use a placeholder `did:iota:0x0000000000000000000000000000000000000000000000000000000000000000` to reference -the DID inside the document. - -::: - -
- - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/0_create_did.rs#L59-L71 -``` - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/0_create_did.ts#L55C44-L65 -``` - - - -
- -### 3. Construct a New Alias Output - -Next, you need to construct a new [Alias Output](../../explanations/about-alias-outputs.mdx) that includes the -DID Document in the [State Metadata](../../explanations/about-alias-outputs.mdx). -The created Alias Output contains an encoded version of the DID Document in its `State Metadata`, and has the state -controller and the governor set to the generated Ed25519 address. - -Note that controllers don't need to be Ed25519 addresses, they can be any type of output. -However, they must be unlocked in order perform a state or governance transition when the DID Document is updated or destroyed. - -
- - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/0_create_did.rs#L75 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/0_create_did.ts#L70 -``` - - - -
- -### 4. Publish the generated Alias Output. - -The byte cost for the document is automatically calculated and a new transaction is published that includes -the Basic Output as input and the newly generated Alias Output as output, as well as another Basic Output which contains -the remaining tokens. - -The DID is only known once the [Alias Output](../../explanations/about-alias-outputs.mdx) is successfully published, -since the DID's [Tag](../../../../references/iota-identity/iota-did-method-spec.mdx#iota-tag) contains the -[Alias ID](../../explanations/about-alias-outputs.mdx#alias-id). -Once the transaction is confirmed, the `Alias ID` is assigned, and the DID can be derived from it, -the DID Document is stored on the ledger and can be [resolved](resolve.mdx) using any node. - -
- - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/0_create_did.rs#L78 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/0_create_did.ts#L74 -``` - - - -
- -## Full Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/0_create_did.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/0_create_did.ts -``` - - - - -## Running Examples Locally - -In order to run the examples, you will need to run the IOTA Sandbox locally. - -If you want to use something different, you will need to modify the API and faucet endpoints in the examples to match your -setup. diff --git a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/delete.mdx b/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/delete.mdx deleted file mode 100644 index 0187ff47d29..00000000000 --- a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/delete.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Delete an IOTA Identity -sidebar_label: Delete -description: How to deactivate or destroy an IOTA Identity -image: /img/identity/icon.png -tags: - - how-to - - identity - - did ---- - - -There are two approaches to delete an IOTA Identity, with different implications: - -- [Deactivate](#deactivate) -- [Destroy](#destroy) - -## Deactivate - -As detailed in the [IOTA DID Method Specification](../../../../references/iota-identity/iota-did-method-spec.mdx#deactivate), -the state controller of an IOTA Identity may [deactivate](https://www.w3.org/TR/did-core/#did-document-metadata) it by publishing an update that either: - -- deletes the contents of the DID Document entirely, leaving the state metadata empty, OR -- sets the `deactivated` field in the DID Document metadata to `true`. - -In both cases, the DID Document will be marked as `deactivated` when resolved. - -:::tip Reversible - -The identity can be reactivated at any time, by publishing an update restoring the DID Document's contents, -or unsetting the `deactivated` field in the metadata respectively, depending on how it was initially deactivated. - -Note that if the governor is different from the state controller, it cannot deactivate an identity directly because -it is disallowed from updating the DID Document, but it may [destroy](#destroy) it. - -::: - -### Example - -The following example demonstrates deactivating and reactivating an IOTA DID Document, -and optionally reclaiming the storage deposit. - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/3_deactivate_did.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts -``` - - - - -## Destroy - -Alternatively, you can [destroy](../../../../references/iota-identity/iota-did-method-spec.mdx#destroy) an IOTA Identity can be permanently. - -:::warning Irreversible - -Destroying an IOTA Identity is permanent and irreversible. - -::: - -This is achieved by the governor of a DID publishing a transaction consuming the [Alias Output](../../../../references/iota-identity/iota-did-method-spec.mdx#alias-output) -containing the IOTA DID Document, without a corresponding Alias Output on the output side. - -Any coins and tokens in the Alias Output are reclaimed and can be sent to another address. - -:::warning - -Note that historical versions may still be stored off-ledger, or on a permanode, -so sensitive or Personal Identifiable Information (PII) should NEVER be stored in a DID Document. - -::: - -Even with a previous version available, a destroyed DID can never be restored. - -### Example - -The following example demonstrates how a governor destroys an IOTA Identity and sends the storage deposit back to itself. - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/4_delete_did.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/4_delete_did.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/resolve.mdx b/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/resolve.mdx deleted file mode 100644 index 77ecee4a621..00000000000 --- a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/resolve.mdx +++ /dev/null @@ -1,191 +0,0 @@ ---- -title: Resolve an IOTA Identity -sidebar_label: Resolve -description: Explain how resolving works including arguments -image: /img/identity/icon.png -tags: - - how-to - - identity - - did ---- - - -DID resolution is the process of fetching and decoding a [DID Document](https://www.w3.org/TR/did-core/#dfn-did-documents) corresponding to a given [DID](https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers). -The [IOTA Identity framework](https://github.com/iotaledger/identity.rs) supports resolving DID Documents that are -stored on the IOTA and Shimmer networks and enables users to plug in handlers for additional methods. - -This is similar to, but not to be confused with, -the [W3C DID Resolution specification](https://w3c-ccg.github.io/did-resolution/), -which defines function signatures for resolution in the context of web or REST APIs, -whereas the IOTA Identity framework provides strongly-typed resolution for a better developer experience. - -This functionality is primarily provided by the `Resolver`, which can: - -- [Resolve IOTA DID Documents](#resolving-an-iota-did). -- [Resolve DID Documents from multiple DID methods](#resolving-multiple-did-methods). -- Resolve the DID Documents referenced in a verifiable presentation or credential. - -## Resolving an IOTA DID - -The following examples demonstrate how to resolve an IOTA DID Document from its DID. - -### Resolver - -Once you have configured a `Resolver` with a `Client`, it will resolve -IOTA DID Documents according to the read procedure defined in the [IOTA DID Method Specification](../../../../references/iota-identity/iota-did-method-spec.mdx#read). -It fetches the latest [Alias Output](../../../../references/iota-identity/iota-did-method-spec.mdx#alias-output) from the network specified in the DID (see [DID Format](../../../../references/iota-identity/iota-did-method-spec.mdx#did-format)), -then extracts and validates the DID Document from it. - - - - -```rust -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use identity_iota::resolver::Resolver; -use iota_sdk::client::Client; - -#[tokio::main] -async fn main() -> anyhow::Result<()>{ - // Configure a client for the Shimmer testnet "rms". - let node_url = "https://api.testnet.shimmer.network/"; - let client = Client::builder() - .with_primary_node(node_url, None)? - .finish()?; - - // Construct a resolver using the client. - let mut resolver = Resolver::::new(); - resolver.attach_iota_handler(client); - - // Parse the DID and resolve its DID Document. - let did = IotaDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47")?; - let document: IotaDocument = resolver.resolve(&did).await?; - - Ok(()) -} -``` - - - - -```js -const { Resolver, IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); -const { Client } = require('@iota/client-wasm/node'); - -// Configure a client for the Shimmer testnet "rms". -const nodeUrl = 'https://api.testnet.shimmer.network/'; -const client = new Client({ - primaryNode: nodeUrl, - localPow: true, -}); -const didClient = new IotaIdentityClient(client); - -// Construct a resolver using the client. -const resolver = new Resolver({ - client: didClient, -}); - -// Resolve the given did -const did = 'did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47'; -const document = await resolver.resolve(did); -``` - - - - -### Client - -You can also use the `Client` directly to resolve individual DIDs from its configured network. - - - - -```rust -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use identity_iota::iota::IotaIdentityClientExt; -use iota_sdk::client::Client; - -#[tokio::main] -async fn main() -> anyhow::Result<()>{ - // Configure a client for the Shimmer testnet "rms". - let node_url = "https://api.testnet.shimmer.network/"; - let client = Client::builder() - .with_primary_node(node_url, None)? - .finish()?; - - // Parse the DID and resolve its DID Document. - let did = IotaDID::parse("did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47")?; - let document: IotaDocument = client.resolve_did(&did).await?; - Ok(()) -} -``` - - - - -```js -const { IotaDID, IotaIdentityClient } = require('@iota/identity-wasm/node'); -const { Client } = require('@iota/client-wasm/node'); - -// Configure a client for the Shimmer testnet "rms". -const nodeUrl = 'https://api.testnet.shimmer.network/'; -const client = new Client({ - primaryNode: nodeUrl, - localPow: true, -}); -const didClient = new IotaIdentityClient(client); - -// Parse the DID and resolve its DID Document. -const did = IotaDID.parse( - 'did:iota:rms:0x7b48b06232b8a1e7a31c314cab1ceedb84e2e9dd2b1fae79b67eaa4595f15e47', -); -const document = await didClient.resolveDid(did); -``` - - - - -## Advanced Resolver Configuration - -You can configure the `Resolver` to support many use cases by attaching custom resolution handlers. -This enables the `Resolver` to resolve multiple DID methods, as well as customizing how -a particular DID method (such as the IOTA method) gets resolved. - -This feature is mainly intended to be used together with the Resolver's convenience methods for -handling [verifiable presentations](../verifiable-presentations/create-and-validate.mdx) -and [credentials](./../../explanations/verifiable-credentials.mdx). - -### Resolving Multiple DID Methods - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/5_custom_resolution.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/4_custom_resolution.ts -``` - - - - -## Resolution for Verifiable Presentations - -When validating [verifiable presentations](./../verifiable-presentations/create-and-validate.mdx), you need to -resolve the DID Documents of the [verifiable credential](./../../explanations/verifiable-credentials.mdx) issuers -and presentation holder to verify their signatures. - -Resolving the necessary DID Documents is -[performed automatically when verifying presentations via the `Resolver`](../verifiable-presentations/create-and-validate.mdx#example-code) - -When direct access to these DID Documents is desired, the `Resolver` also provides standalone methods to: - -- Resolve a presentation holder's DID Document. -- Resolve the DID Documents of the issuers of the credentials in a verifiable presentation. -- Resolve the issuer's DID Document for a given verifiable credential. diff --git a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/update.mdx b/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/update.mdx deleted file mode 100644 index df8e5903dcc..00000000000 --- a/docs/content/developer/iota-identity/how-tos/decentralized-identifiers/update.mdx +++ /dev/null @@ -1,530 +0,0 @@ ---- -sidebar_label: Update -description: How DID Documents can be manipulated and how updates should be published -image: /img/identity/icon.png -tags: - - how-to - - identity - - did ---- - - -# Update DID Documents - -You can extend DID Documents by adding [verification methods](#verification-methods), [services](#services) and custom properties. -A verification method adds public keys, which can be used to digitally sign things like a DID message or a verifiable credential, -while a service can provide metadata around the identity via URIs. - -## Verification Methods - -As demonstrated by the [example](#full-example-code) below, the IOTA Identity framework offers easy-to-use methods for adding -[verification methods](https://www.w3.org/TR/did-core/#verification-methods). - -### Properties - -You can specify the following properties for a verification method: - -- **id**: A [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for the verification method. You can specify it by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). -- **type**: Specifies the type of the verification method. The framework supports `Ed25519` and `X25519` key types. This property is automatically filled by the framework when specifying the verification material. -- **publicKeyMultibase**: A multi-base encoded public key which concludes the [verification material](https://www.w3.org/TR/did-core/#verification-material). This can be automatically generated by the framework or manually provided by users. - -## Verification Relationships - -[Verification relationships](https://www.w3.org/TR/did-core/#verification-relationships) express the relationship between the DID subject and the verification method. -You can use it to specify the purpose of the verification method. - -### Relationships - -The Identity Framework supports the following relationships: - -- **[Authentication](https://www.w3.org/TR/did-core/#authentication)**: Used to specify authentication methods for the DID subject. -- **[Assertion](https://www.w3.org/TR/did-core/#assertion)**: Used to verify verifiable credentials. -- **[Key Agreement](https://www.w3.org/TR/did-core/#assertion)**: Used for establishing secure communication channels. -- **[Capability Invocation](https://www.w3.org/TR/did-core/#capability-invocation)**: Can be used to authorize updates to the DID Document. -- **[Capability Delegation](https://www.w3.org/TR/did-core/#capability-delegation)**: A mechanism to delegate cryptographic capability to another party. - -Verification methods can be either [embedded or referenced](https://www.w3.org/TR/did-core/#referring-to-verification-methods). Referencing verification -methods allows them to be used by more than one verification relationship. -When you create a verification method using the Identity Framework, specifying the `MethodScope` option will result in an embedded verification method. -Leaving that option unset will create the verification method as a map entry of the `verificationMethod` property. -You can also add verification relationships afterward using references. - -:::note - -Updates to the DID Document are done through a state transition of the [Alias Output](../../../../references/iota-identity/iota-did-method-spec.mdx#alias-output) by its state controller. -The public key or address of the state controller does not need to be a verification method in the DID Document, -since it is defined in the containing Alias Output. - -::: - -## Services - -[Services](https://www.w3.org/TR/did-core/#services) allow you to add other ways of communicating with the DID subject. -An endpoint included in the DID Document can offer a way of reaching services for different purposes -like authentication, communicating, and discovery. - -### Properties - -You can specify the following properties for a service: - -- **id**: A [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for referencing the service in the DID document. You can specify it by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). -- **type**: A string used to maximize interoperability between services. The framework does not perform any checks on the content of this string. -- **serviceEndpoint**: A URL that points to the service endpoint. - -## Create Identity - -Before you can update anything, you will need to [create an Identity](./create.mdx). - - - - -```rust - // Create a new client to interact with the IOTA ledger. - let client: Client = Client::builder() - .with_primary_node(API_ENDPOINT, None)? - .finish() - .await?; - - // Create a new secret manager backed by a Stronghold. - let mut secret_manager: SecretManager = SecretManager::Stronghold( - StrongholdSecretManager::builder() - .password(Password::from("secure_password".to_owned())) - .build(random_stronghold_path())?, - ); - - // Create a new DID in an Alias Output for us to modify. - let storage: MemStorage = MemStorage::new(JwkMemStore::new(), KeyIdMemstore::new()); - let (_, document, fragment_1): (Address, IotaDocument, String) = - create_did(&client, &mut secret_manager, &storage).await?; - let did: IotaDID = document.id().clone(); - -``` - - - - -```js - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document, fragment } = await createDid( - client, - secretManager, - storage, - ); -``` - - - - -This creates and publishes an Alias Output containing a DID Document with one verification method. - -```json -{ - "doc": { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "verificationMethod": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "crv": "Ed25519", - "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" - } - } - ] - }, - "meta": { - "created": "2023-11-16T20:40:03Z", - "updated": "2023-11-16T20:40:03Z", - "governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd", - "stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd" - } -} -``` - -## Add a Verification Method - - - - -```rust - // Insert a new Ed25519 verification method in the DID document. - let fragment_2: String = document - .generate_method( - &storage, - JwkMemStore::ED25519_KEY_TYPE, - JwsAlgorithm::EdDSA, - None, - MethodScope::VerificationMethod, - ) - .await?; -``` - - - - -```js -// Insert a new Ed25519 verification method in the DID document. -await document.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - '#key-2', - MethodScope.VerificationMethod(), -); -``` - - - - -This creates a new verification method that includes a newly generated Ed25519 public key. - -```json -{ - "doc": { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "verificationMethod": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "crv": "Ed25519", - "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" - } - }, - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "crv": "Ed25519", - "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" - } - } - ] - }, - "meta": { - "created": "2023-11-16T20:40:03Z", - "updated": "2023-11-16T20:40:03Z", - "governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd", - "stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd" - } -} -``` - -Notice that these changes to the document are not [published](#publish-your-updates) yet. - -## Add Verification Relationships - -You can attach verification relationships to a verification method by referencing its fragment. - - - - -```rust -// Attach a new method relationship to the inserted method. -document.attach_method_relationship( - &document.id().to_url().join(format!("#{fragment_2}"))?, - MethodRelationship::Authentication, -)?; -``` - - - - -```js -// Attach a new method relationship to the inserted method. -document.attach_method_relationship( - &document.id().to_url().join(format!("#{fragment_2}"))?, - MethodRelationship::Authentication, -)?; -``` - - - - -This will add `Authentication` relationship to the verification method with the fragment `key-1`. -Note that `Authentication` references the already included `key-2` verification method: - -```json {12,19} -{ - "doc": { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "verificationMethod": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "crv": "Ed25519", - "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" - } - }, - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "crv": "Ed25519", - "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" - } - } - ], - "authentication": [ - "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" - ] - }, - "meta": { - "created": "2023-11-16T20:40:03Z", - "updated": "2023-11-16T20:40:03Z", - "governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd", - "stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd" - } -} -``` - -## Add a Service - -You can also add custom properties can to a service by setting `properties`: - - - - -```rust - // Add a new Service. - let service: Service = Service::from_json_value(json!({ - "id": document.id().to_url().join("#linked-domain")?, - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - }))?; - assert!(document.insert_service(service).is_ok()); - document.metadata.updated = Some(Timestamp::now_utc()); -``` - - - - -```js - // Add a new Service. - const service: Service = new Service({ - id: did.join("#linked-domain"), - type: "LinkedDomains", - serviceEndpoint: "https://iota.org/", - }); - document.insertService(service); - document.setMetadataUpdated(Timestamp.nowUTC()); -``` - - - - -The updated Document with the newly created service looks as follows. - -```json {21-27} -{ - "doc": { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "verificationMethod": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", - "crv": "Ed25519", - "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" - } - }, - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "crv": "Ed25519", - "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" - } - } - ], - "authentication": [ - "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" - ], - "service": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - } - ] - }, - "meta": { - "created": "2023-11-16T20:40:03Z", - "updated": "2023-11-16T20:40:08Z", - "governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd", - "stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd" - } -} -``` - -## Remove a Verification Method - -You can also remove verification methods at any time using the following snippet: - - - - -```rust -// Remove a verification method. -let original_method: DIDUrl = document.resolve_method(fragment_1.as_str(), None).unwrap().id().clone(); -document.purge_method(&storage, &original_method).await.unwrap(); -``` - - - - -```js -// Remove a verification method. -let originalMethod = document.resolveMethod(fragment) as VerificationMethod; -await document.purgeMethod(storage, originalMethod?.id()); -``` - - - - -This removes the original verification method with the fragment `key-1`. - -```json -{ - "doc": { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "verificationMethod": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", - "crv": "Ed25519", - "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" - } - } - ], - "authentication": [ - "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" - ], - "service": [ - { - "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - } - ] - }, - "meta": { - "created": "2023-11-16T20:40:03Z", - "updated": "2023-11-16T20:40:08Z", - "governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd", - "stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd" - } -} -``` - -## Publish Your Updates - -Publish the updated DID Document inside the Alias Output taking into account the increase in the storage deposit needed. - - - - -```rust - // Resolve the latest output and update it with the given document. - let alias_output: AliasOutput = client.update_did_output(document.clone()).await?; - - // Because the size of the DID document increased, we have to increase the allocated storage deposit. - // This increases the deposit amount to the new minimum. - let rent_structure: RentStructure = client.get_rent_structure().await?; - let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output) - .with_minimum_storage_deposit(rent_structure) - .finish()?; - - // Publish the updated Alias Output. - let updated: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?; -``` - - - - -```js - // Resolve the latest output and update it with the given document. - let aliasOutput: AliasOutput = await didClient.updateDidOutput(document); - - // Because the size of the DID document increased, we have to increase the allocated storage deposit. - // This increases the deposit amount to the new minimum. - const rentStructure: IRent = await didClient.getRentStructure(); - - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - // Publish the output. - const updated: IotaDocument = await didClient.publishDidOutput(secretManager, aliasOutput); -``` - - - - -## Full Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/1_update_did.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/1_update_did.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/domain-linkage/create-and-verify.mdx b/docs/content/developer/iota-identity/how-tos/domain-linkage/create-and-verify.mdx deleted file mode 100644 index 2c222ef6403..00000000000 --- a/docs/content/developer/iota-identity/how-tos/domain-linkage/create-and-verify.mdx +++ /dev/null @@ -1,174 +0,0 @@ ---- -description: How to link a domain and a DID -sidebar_label: Create and Verify -image: /img/identity/icon.png -tags: - - how-to - - identity ---- - -# Domain Linkage - -:::info -To use Domain Linkage in Rust you have to enable the `domain-linkage` feature. -::: - -## Overview - -Domain Linkage can provide proof for a connection between a DID and a domain being controlled by the same entity. -This linkage can transfer trust from a domain to a DID and vice versa. -For instance, if an entity trusts a domain, it can also trust the linked DID and all documents signed by -the verification methods included in the DID Document. - -A use case could be a verifier that trusts `www.example.com`, and receives a verifiable presentation issued by `did:foo:abc`. -If `did:foo:abc` is linked to `www.example.com`, the verifier can trust that the verifiable presentation is issued by -the same entity controlling `www.example.com`. -The DIF has approved a [Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/) draft to standardize this connection by introducing -the [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource) and the [Linked Domain Service Endpoint](https://identity.foundation/.well-known/resources/did-configuration/#linked-domain-service-endpoint). - -![Identity getting started](/img/iota-identity/domain-linkage-diagram.png) - -### DID Configuration Resource - -Suppose that a DID `did:foo:example` with the following DID Document only contains one `verificationMethod`, `key-1`: - -```json {5} -{ - "id": "did:foo:abc", - "verificationMethod": [ - { - "id": "did:foo:abc#key-1", - "controller": "did:foo:abc", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "zDShpHKXkcHKHcF8CnGAA1UqyyuEPRNz1XFEuggbWJQSq" - } - ] - }, -``` - -The domain `https://www.example.com` represents the same entity and needs to be linked to increase trust in the DID. - -To establish this link, you must create a [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource), -and make it available on the [DID Configuration URL](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-uri). -In this case it's `https://example.com/.well-known/did-configuration.json`. - -The [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource) is a JSON-LD object containing verifiable credentials called `Domain Linkage Credentials`. -Each credential represents a linkage to a single DID. - -:::note - -Note that one `DID Configuration Resource` can include multiple `Domain Linkage Credentials`, -effectively linking the same domain to multiple DIDs. - -::: - -In this example, the domain `https://www.example.com` needs to be linked to the DID `did:foo:abc`. -This means that the `DID Configuration Resource` will have one `Domain Linkage Credential`. -This credential must have the following properties: - -- Its `type` includes `DomainLinkageCredential`. -- It includes the DID Configuration context. -- The `credentialSubject` must be the DID `did:foo:abc` and references the domain `https://www.example.com`. -- The issuer is the DID itself `did:foo:abc`. -- It is signed by a key material included in the DID Document, in this case `did:foo:abc#key-1`. - -```json -{ - "@context": "https://identity.foundation/.well-known/did-configuration/v1", - "linked_dids": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://identity.foundation/.well-known/did-configuration/v1" - ], - "type": ["VerifiableCredential", "DomainLinkageCredential"], - "credentialSubject": { - "id": "did:foo:abc", - "origin": "https://www.example.com/" - }, - "issuer": "did:foo:abc", - "issuanceDate": "2023-02-09T22:14:15Z", - "expirationDate": "2024-02-09T22:14:15Z", - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:foo:abc#key-1", - "signatureValue": "4SvYqo3YoArfW7r7qKfN7RUJdZnBteb166KE4UkX8MNdbp5UW6YbykneAzvjyRmf5EVQ9bnP9cS5sbEPUn2uaAcB" - } - } - ] -} -``` - -Now this `DID Configuration Resource` must be made available on `https://example.com/.well-known/did-configuration.json`, -which establishes the linkage. - -### Linked Domain Service Endpoint - -By having a domain, one can discover what DIDs are linked to it by fetching the `DID Configuration Resource` and -investigating the `Domain Linkage Credentials`. - -If you want to enable discovery from the other direction, that is, if you have a DID and want to discover which -domains are linked to it, you can add a [Linked Domain Service Endpoint](https://identity.foundation/.well-known/resources/did-configuration/#linked-domain-service-endpoint) to the DID Document. -The DID Document from this example will be extended as follows to enable discovery of `https://www.example.com`: - -```json {11-17} -{ - "id": "did:foo:abc", - "verificationMethod": [ - { - "id": "did:foo:abc#key-1", - "controller": "did:foo:abc", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "zDShpHKXkcHKHcF8CnGAA1UqyyuEPRNz1XFEuggbWJQSq" - } - ], - "service": [ - { - "id": "did:foo:abc#domain-linkage", - "type": "LinkedDomains", - "serviceEndpoint": "https://www.example.com/" - } - ] -} -``` - -:::note -Note that a DID Document can have multiple `Linked Domain Services` and each service can link to multiple domains. -::: - -### Verifying a DID and Domain Linkage - -As mentioned above, you can discover the Domain Linkage from either direction. -However, verifying the linkage in both cases involves only verifying the DID Configuration Resource. -The process is as follows: - -1. Fetch `DID Configuration Resource` from `https://www.example.com/.well-known/did-configuration.json`. -2. Resolve the DID Document of `did:foo:abc`. -3. Verify the `DID Configuration Resource` and its `Domain Linkage Credential` that references `did:foo:abc`. - - -:::tip About DID Configuration Resource Verification - -You can learn more -[about DID Configuration Resource Verification on the Identity Foundation website](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource-verification). - -::: - -## Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/6_domain_linkage.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/key-storage.mdx b/docs/content/developer/iota-identity/how-tos/key-storage.mdx deleted file mode 100644 index 9949e9418e9..00000000000 --- a/docs/content/developer/iota-identity/how-tos/key-storage.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Key Storage -sidebar_label: Key Storage -description: Explain the use of the storage interfaces and how they can be implemented -image: /img/identity/icon.png -tags: - - how-to - - identity ---- - - -## Introduction - -The `JwkDocumentExt` API allows you to modify a DID document, for example, adding new verification methods. -It enables storing the secrets that verification methods represent securely. -It does so using the two storage interfaces, the `JwkStorage` and `KeyIdStorage`. -We refer to both of these as the **key storage**. - -The main idea behind the key storage is strongly inspired by the architecture of key management systems (KMS) -or secure enclaves: once private keys are entered into the system, they can never be retrieved again. -Instead, all operations using the key will have to go through that system. - -This approach allows the key storage to be architected more securely than simply storing and loading private keys from -a regular database. -Of course, security is directly dependent on the concrete implementation, -which is why we provide [Stronghold](https://github.com/iotaledger/stronghold.rs/), a best-effort in-software enclave, by default. - -However, there are cases where one cannot use `Stronghold`, -or may want to integrate key management of identities into their own KMS or similar, -which is why the key storage is an abstraction over such systems. -Any implementation of a key storage can be used by the `JwkDocumentExt` API. - -The two interfaces making up the key storage have two respective responsibilities. - -:::info - -Even though there are two separate interfaces, you can implement them using the same backing storage. - -::: - -## Function Overview - -A brief overview of those functions: - -- `JwkStorage`: CRUD and signing operations on [JSON Web Keys](https://www.rfc-editor.org/rfc/rfc7517). - - `generate`: Generate a new key represented as a JSON Web Key. - - `insert`: Insert an existing JSON Web Key into the storage. - - `sign`: Signs the provided data using the stored private key. - - `delete`: Permanently deletes a key. - - `exists`: Returns whether a key exists. -- `KeyIdStorage`: Stores the mappings from verification methods to their corresponding key identifier in the `JwkStorage`. - - `insert_key_id`: Inserts a mapping from a verification method identifier to a key identifier. - - `get_key_id`: Returns the key identifier for a given verification method identifier. - - `delete_key_id`: Deletes a mapping. - -## Key Identifier - -A `JwkStorage` stores and operates on keys, so they must be identified. -In general, Key Management Systems use some form of an identifier for their keys. -To abstract over those, the `JwkStorage` interface has a general-purpose `KeyId` type, -which is effectively a wrapper around a string. - -A `KeyIdStorage` is needed to store the key id that represents the private key for a given verification method. -To that end, a verification method itself must be identified. - -While within a document, each fragment must be unique, the same is not true given multiple documents, -so we cannot rely only on fragments if we don't want to partition the `KeyIdStorage` by DID. -The solution to this is using a `MethodDigest`, a hash over a verification method. - -When following best security practices, each verification method has its own associated key and, thus, a unique public key. -That, plus the fragment of a method, ensures the `MethodDigest` is unique. - -So, in essence, a `JwkStorage` stores a `KeyId -> JWK` mapping while a `KeyIdStorage` stores a `MethodDigest -> KeyId` mapping. - -:::caution - -Given the construction and limitations of the method digest, -no two documents should contain a method that shares both the same fragment and public key. -This should not happen under typical circumstances, but it is good to keep it in mind. - -::: - -## Key Types - -To express what key types a given `JwkStorage` implementation supports, you should use the `KeyType`, -which is another simple wrapper around a string. - -The reason for this design might seem odd in Rust, given the existence of associated types. -This more simplistic design is necessary to accommodate implementing the interface via the bindings to the library. - -Implementations are expected to export constants of the key types they support, -so users have an easy way to discover the supported types. -In general, storage implementations are free to support any [JSON Web Algorithm](https://www.rfc-editor.org/rfc/rfc7518.html)-compatible key. -However, the recommended default used by IOTA Identity is the `EdDSA` algorithm with curve `Ed25519`. - -## Implementation - -The IOTA Identity library ships two implementations of key storage. -The `JwkMemStore` and `KeyIdMemstore` are insecure in-memory implementations -intended as example implementations and for testing. - -The default key storage implementation is `Stronghold`, -which is an example of a storage that implements both storage interfaces simultaneously. -[`Stronghold`](https://github.com/iotaledger/stronghold.rs/) may be interesting for implementers to look at, -as it needs to deal with some challenges the in-memory version does not have to deal with. Note that the `Stronghold` implementation is only available in Rust. - -## Examples - -This section shows the Rust and TypeScript `Memstore` implementations. - -### `JwkMemStore` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/lib/jwk_storage.ts -``` - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/identity_storage/src/key_storage/memstore.rs -``` - - - - -### `KeyIdMemstore` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/lib/key_id_storage.ts -``` - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/identity_storage/src/key_id_storage/memstore.rs -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/create.mdx b/docs/content/developer/iota-identity/how-tos/verifiable-credentials/create.mdx deleted file mode 100644 index 5110564400e..00000000000 --- a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/create.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Create a Verifiable Credential -sidebar_label: Create and Sign -description: Explain how a VC is created and verified -image: /img/identity/icon.png -tags: - - how-to - - identity - - verifiable-credentials ---- - -A [Verifiable Credential (VC)](./../../explanations/verifiable-credentials.mdx) can represent all -information that a physical credential represents, such as a passport or university -degree. However, by allowing other parties to cryptographically verify the authorship -and integrity of the claims, verifiable credentials can be seen as more tamper-evident -and more trustworthy than their physical counterparts. - -## Verifiable Credential Properties - -In the IOTA Identity Framework, you can create a Verifiable Credential with the following properties: - -- [**Context**](https://www.w3.org/TR/vc-data-model/#contexts): List of JSON-LD context URIs. Includes `"https://www.w3.org/2018/credentials/v1"` by default. -- [**Types**](https://www.w3.org/TR/vc-data-model/#types): List of types describing the credential. Includes `"VerifiableCredential"` by default. -- [**Subject**](https://www.w3.org/TR/vc-data-model/#credential-subject): The issuer's claims; a set of objects that contain one or more properties that are each related to a subject. -- [**Issuer**](https://www.w3.org/TR/vc-data-model/#issuer): The identifier of the issuer, typically their [DID](../../explanations/decentralized-identifiers.mdx). -- [**ID**](https://www.w3.org/TR/vc-data-model/#identifiers): Optional URI identifier for the credential. -- [**Issuance Date**](https://www.w3.org/TR/vc-data-model/#issuance-date): Timestamp for expressing the date and time when a credential becomes valid. -- [**Expiration Date**](https://www.w3.org/TR/vc-data-model/#expiration): Optional timestamp for expressing the date and time when a credential ceases to be valid. -- [**Status**](https://www.w3.org/TR/vc-data-model/#status): Optional information used to determine the current status of a credential, i.e. whether or not it has been [revoked](./revocation.mdx). -- [**Schema**](https://www.w3.org/TR/vc-data-model/#data-schemas): Optional list of objects specifying the schema that the data must conform to. -- [**Refresh Service**](https://www.w3.org/TR/vc-data-model/#refreshing): Optional link to a service where the recipient may refresh the included credentials. -- [**Terms of Use**](https://www.w3.org/TR/vc-data-model/#terms-of-use): Optional list of policies defining obligations, prohibitions, or permissions of the presentation recipient. -- [**Evidence**](https://www.w3.org/TR/vc-data-model/#evidence): Optional list of objects that can be used by the issuer to provide the verifier with additional supporting information in a verifiable credential. -- [**Non-Transferable**](https://www.w3.org/TR/vc-data-model/#nontransferable-property): Optional flag that indicates that a verifiable credential must only be encapsulated in a [verifiable presentation](./../../explanations/verifiable-presentations.mdx) whose proof was issued by the credential subject. - - - - -## Signing {#signing} - -After preparing the verifiable credential, the issuer creates a signed JWT containing VC in the claims using one of their private keys. This is what allows verifiers to validate the credential independently using the corresponding public key from the issuer's DID Document. - -## Validation - -Verifiers should ensure certain credential properties are valid when receiving one or more in a [verifiable presentation](./../../explanations/verifiable-presentations.mdx). Both issuers and holders may also wish to validate their credentials, particularly directly after creating or receiving one. Validation may be performed at any point in time and can be a useful way of checking whether a credential has expired or been revoked. - -### Validation Checks - -The IOTA Identity Framework supports the following checks during credential validation: - -- **Semantic structure**: Ensures the credential adheres to the specification. -- **Signature**: Verifies the JWS against the issuer's DID Document. -- **Optional validations**: Additional checks on credential properties, and the signature can be configured by specifying [Validation Options](#validation-options). - -### Validation Options - -These options specify conditions that specific properties in a credential must satisfy. - -- **Expiration Date**: Check that the [`expirationDate`](https://www.w3.org/TR/vc-data-model/#expiration) property, if present, is not before a specific date-time. Defaults to the current datetime if unset. -- **Issuance Date**: Check that [`issuanceDate`](https://www.w3.org/TR/vc-data-model/#issuance-date) property, if present, is not after a specific date-time. Defaults to the current datetime if unset. -- **Verifier Options**: Validates aspects of the credential signature. - -## Sharing Verifiable Credentials - -A [verifiable presentation](./../../explanations/verifiable-presentations.mdx) is the recommended data format for sharing one or more verifiable credentials, -as it provides cryptographic means of proving the DID of the holder presenting them, -and for enforcing [subject-holder relationships](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships). - -## Example - -The following code showcases how an issuer can [create, sign](#create-and-sign-a-vc), -and [validate](#validate-a-vc) a verifiable credential. -In this example, the issuer signs a `UniversityDegreeCredential` with Alice's name and DID. - -### Create and Sign a VC - - -
- - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/5_create_vc.rs#L67-L98 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/5_create_vc.ts#L51-L74 -``` - - - -
- -### Validate a VC - -
- - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/5_create_vc.rs#L105-L113 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/5_create_vc.ts#L83-L88 -``` - - - -
- -This Verifiable Credential can be [verified by anyone](./../../explanations/verifiable-presentations.mdx), -allowing Alice to take control of it and share it with anyone. - -### Full Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/5_create_vc.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/5_create_vc.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/revocation.mdx b/docs/content/developer/iota-identity/how-tos/verifiable-credentials/revocation.mdx deleted file mode 100644 index 318998016c0..00000000000 --- a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/revocation.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -sidebar_label: Revoke -description: Explain how a VC can be revoked -image: /img/identity/icon.png -tags: - - how-to - - identity - - verifiable-credentials ---- - - -# Revoke a Verifiable Credential - -The [example](#full-example-code) below demonstrates two methods that an issuer can use to revoke a verifiable credential -using the IOTA Identity Framework: - -1. By using the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) field in a credential and linking - to a [revocation method](#revocation-methods). -2. By [removing the verification method](#removing-the-verification-method) that signed the credential. - This invalidates all credentials that were signed with that verification method. - -## Revocation methods - -The IOTA Identity Framework supports two different revocation methods: `RevocationBitmap2022` and `StatusList2021`. - -### Revocation Bitmap - -[RevocationBitmap2022](../../../../references/iota-identity/revocation-bitmap-2022.mdx) is the default credential revocation method used in the IOTA Identity Framework. It allows -issuers to control whether a credential is _valid_ or _revoked_. To do so, a revocation list (represented -as a bitmap) is stored in the issuer's DID document. -When a credential is issued, a unique index from the issuer's revocation list -is chosen, linking the credential's status to the value of the list entry. To change the status of a credential, the issuer -simply updates the corresponding entry in its revocation list. - -With `RevocationBitmap2022` the `identity.rs` library completely handles the processes required to handle credentials revocation; -from creation and storage of the revocation list to its lookup. -This makes `RevocationBitmap2022` the preferred way for users to handle credential revocation, but it requires sufficient -funds to rent out the required on-tangle space. - -:::note - -DLT's size constraints limit the size of the revocation list. With the assumption of only one such revocation list -per the issuer's DID document, one may expect to be able to handle roughly 50k entries. - -::: - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/7_revoke_vc.rs#L167 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts#L156 -``` - - - - -If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, -if it is 0 (zero) it is not revoked. - -For example, with this approach the issuer adds an index to a credential in the `credentialStatus` field, such as `"5"`. -This part of the credential might then look like this: - -```json -"credentialStatus": { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", - "type": "RevocationBitmap2022", - "revocationBitmapIndex": "5" -}, -``` - -The verifier uses the `id` field (`did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation`) to look up the -service in the issuer's DID document: - -```json -{ - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", - "type": "RevocationBitmap2022", - "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" -} -``` - -During verification, the verifier decodes the revocation bitmap embedded in the `data` URL. -This bitmap written as a bitstring looks like this: `000001`. -Here, the 5th bit is set, which means the credential with that index is revoked, -while all other credentials aren't revoked. - -### StatusList2021 - -[StatusList2021](https://www.w3.org/TR/2023/WD-vc-status-list-20230427) offers similar functionalities to `RevocationBitmap2022` -but in a more flexible and scalable way. -The main difference is that `StatusList2021` is completely agnostic in regards to how the issuer's status list -is stored and fetched, as long as its location can be encoded through a URL. For instance, the status list -can be made available over HTTP (e.g. `https://example.com/credentials/status`) or through the -Interplanetary File System (e.g. `ipfs://QmXDWGdVBhbDoXXzKNMhJk5ejnZgxpMBVzW4EhQaHPD3Mi`). - -This flexibility, although it requires the issuer to manually fetch and update its status list, allows for an arbitrary number of -entries in the status list, in contrast with `RevocationBitmap2022`, where the length of the list is limited by the DLT's constraints. - -Furthermore, `StatusList2021` introduces a new credential state: _suspended_. Suspended credentials are credentials that a validator will not accept as _valid_, but that might become valid again in the future. Instead, _revoked_ credentials **cannot** ever -be valid again, as the _revoked_ state is irreversible. - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/8_status_list_2021.rs#L86-L90 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/7_status_list_2021.ts#L72-L76 -``` - - - - -First, an issuer creates a credential that encodes a certain status list, specifying its purpose (either `revocation` or `suspension`) -and the location at which it will be available (`https://example.com/credentials/status` in this case). After creation, the issuer -must make the credential available at the chosen URL so that verifiers can fetch it. - -Upon issuing a credential, to revoke it or suspend it later, the issuer sets the `credentialStatus` field, linking -to an entry in its status list. The snippet below shows what `credentialStatus` would look like when linking to the previously created -status list credential. - -```json -{ - "id": "https://example.com/credentials/status#94567", - "type": "StatusList2021Entry", - "statusPurpose": "revocation", - "statusListIndex": "94567", - "statusListCredential": "https://example.com/credentials/status" -} -``` - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/8_status_list_2021.rs#L173 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/7_status_list_2021.ts#L147 -``` - - - - -To set the status of a credential, the issuer retrieves the status list credential and sets the value of the chosen entry index. - -## Removing the Verification Method - -A less efficient alternative is to remove the verification method that signed the credential from the DID Document of -the issuer. -This means the VC can no longer be validated. - -However, this will also invalidate every VC signed with that verification method, -meaning that the issuer will have to sign every VC with a different key to retain -precise control over which credential is revoked. - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/7_revoke_vc.rs#L197-L204 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts#L192C1-L195 -``` - - - - -## Full Example Code - -The following code exemplifies how you can revoke a [Verifiable Credential (VC)](../../explanations/verifiable-credentials.mdx). - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/7_revoke_vc.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx b/docs/content/developer/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx deleted file mode 100644 index ad49f4be907..00000000000 --- a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -sidebar_label: Selective Disclosure -description: Explain VC with selective disclosure. -image: /img/identity/icon.png -tags: - - how-to - - identity - - verifiable-credentials ---- - - -# Selective Disclosure (SD-JWT) - - -Holders of verifiable credentials may prefer to keep all the information contained within the credential private from a verifier. Instead, they may opt only to share a specific subset of the properties included in the VC. The identity library implements the [IETF Specifications](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html), which outlines a mechanism to enable the selective disclosure of individual properties within the JSON object of JWT claims. - -## Concept - -### Issuance - -During the issuance process, the issuer replaces a subset of the fields in a credential with digests of their salted values and creates a signed JWT. Next, JWT, alongside the plain text disclosures and the salt used for digest creation are sent to the holder. - -### Presentation - -At this stage, the holder can selectively choose which fields to disclose to the verifier. The disclosures are sent in plain text, with the JWT containing the digests to the verifier. - -:::note -Only values replaced by digests through the issuer can be selectively disclosed. The holder **can not** conceal values provided in plain text in the JWT claims. -::: - - -### Validation - -With these values and a valid signature, the verifier is able to reconstruct a Verified Credential (VC) that exclusively contains the information the holder intended to disclose. - -## How It Works - -A SD JWT can be constructed from the following JWT claim of an address credential in accordance with the [VC Data Model v1.1](https://www.w3.org/TR/vc-data-model/#json-web-token): - - -```json -{ - "iss": "did:iota:tst:0x899d07a766f93c2af1a19a3f4583ad338fc94c5d84b6afcadf49b197e1cb693e", - "jti": "https://example.com/credentials/3732", - "nbf": 1705925652, - "sub": "did:iota:tst:0x6c045e1f658197b432cfc7c66350b8781dca50f820e9de0fcdf0029b4b384355", - "vc": { - "@context": "https://www.w3.org/2018/credentials/v1", - "credentialSubject": { - "address": { - "country": "DE", - "locality": "Maxstadt", - "postal_code": "12344", - "street_address": "Weidenstraße 22" - }, - "name": "Alice" - }, - "type": [ - "VerifiableCredential", - "AddressCredential" - ] - } -} - -``` - -The issuer makes the values of "locality", "postal_code", and "street_address" selectively disclosable, giving the holder the freedom to select what details of the address to be disclosed and presented. - -```json -{ - "_sd_alg": "sha-256", - "iss": "did:iota:tst:0x899d07a766f93c2af1a19a3f4583ad338fc94c5d84b6afcadf49b197e1cb693e", - "jti": "https://example.com/credentials/3732", - "nbf": 1705925652, - "sub": "did:iota:tst:0x6c045e1f658197b432cfc7c66350b8781dca50f820e9de0fcdf0029b4b384355", - "vc": { - "@context": "https://www.w3.org/2018/credentials/v1", - "credentialSubject": { - "address": { - "_sd": [ - "8Dai0-GMZgkzmdryGzjYufUaRFkiNWzVsJJdWucwu84", - "jemTNaG_wiHauwmwWiWREsirAlr91qugPds4MA8e2xo", - "iakC9Dfe2r9fGnOaAr_pGg1b7CwITBjcwE7-O7WlMnY" - ], - "country": "DE" - }, - "name": "Alice" - }, - "type": [ - "VerifiableCredential", - "AddressCredential" - ] - } -} -``` - -:::note -The digests are contained in the `_sd` property in `address`. This allows both keys and values to be concealed. -::: - -For further details, see the [example](#full-example-code) below and the [sd-jwt-payload crate](https://github.com/iotaledger/sd-jwt-payload). - -## Presentation format - -The SD-JWT is presented in the following [format](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#section-5): - -> `~~~...~~` - -## Key Binding JWT - -When a verifier receives an SD-JWT, it may be desirable to verify that the presenter's identity matches the holder of the Credential. For that purpose, a [Key Binding JWT (KB-JWT)](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#section-4.3) can be used. - - -- The verifier sends a nonce to the holder. -- The holder creates a JWT containing the nonce and the digest of the issuer-signed JWT and the disclosures 1→N. -- The holder sends the KB-JWT to the verifier as a part of the presentation. -- By verifying the KB-JWT, the verifier ensures the identity of the holder, the integrity of the data, the freshness of the signature, and the intended audience. - - - -## Full Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/7_sd_jwt.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/6_sd_jwt.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx b/docs/content/developer/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx deleted file mode 100644 index f3bbeac029a..00000000000 --- a/docs/content/developer/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -sidebar_label: Zero Knowledge Selective Disclosure -description: Zero Knowledge selectively disclosable VCs. -image: /img/identity/icon.png -tags: - - how-to - - identity - - verifiable-credentials - - zk ---- - - -# Zero Knowledge Selective Disclosure (ZK-SD-VCs) -ZK-SD-VCs allow holders to verify their VCs without having to disclose the entire VC's claim set to verifiers. -This is done through the creation of a Zero Knowledge Proof (ZKP) that guarantees the integrity and authenticity -of the VC, even when only partially disclosed to the verifier. - -:::note -Although ZK-SD-VCs offer similar functionalities to [SD-JWT VCs](selective-disclosure.mdx) - at least on a high level - they rely on completely different -concepts and security concerns. For a user, the most notable difference is the shifted capability of choosing which fields can -be concealed from a verifier. For ZK-SD-VCs it's the holder that has total control over which parts of the credential can be -undisclosed, whereas for SD-JWT VCs it's the issuer that decides which fields may be concealed by the holder. -::: - -## Concepts -### Issuance -The issuer of a ZK-SD-VC creates the credential, signs it using the [BBS+](https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-05.html) signature scheme -and sends both the credential and the signature to the holder. To facilitate this process, the credential is first encoded -as a [JSON Proof Token](https://www.ietf.org/archive/id/draft-ietf-jose-json-proof-token-02.html) (JPT), which is then used as the payload of a -[JSON Web Proof](https://www.ietf.org/archive/id/draft-ietf-jose-json-web-proof-02.html) (JWP) and sent to the holder as JPT. -:::note -JWPs and JPTs can be reasoned about as the Zero Knowledge (ZK) based counterparts of JWSs and JWTs. -::: -In code, this process would look like the following snippet: - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/9_zkp.rs#L114-L141 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/8_zkp.ts#L109-L133 -``` - - - - - -Note how the VC issuer makes no prescription whatsoever regarding the disclosability of the VC's fields. - -### Holder presentation - -Once the holder receives a presentation challenge from a verifier, they construct a selective disclosure presentation for the requested credential -and send it back for verification. For this process the JWP in possession of the holder undergoes a transformation that allows the holder -to conceal any fields from the credentials claims through the creation of a Zero Knowledge Proof (ZKP) of the issuer's signature and becomes a _presented JWP_. -The proof value depends on the selected [JSON Proof Algorithm](https://www.ietf.org/archive/id/draft-ietf-jose-json-proof-algorithms-02.html) (JPA). - - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/9_zkp.rs#L197-L223 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/8_zkp.ts#L178-L199 -``` - - - - -Here's an example presented JWP in its JPT JSON serialization format where the undisclosed values are replaced by `null`: -``` -{ - "payloads": [ - null, - "IkpheSI", - null, - "NDI" - ], - "issuer": "eyJpc3MiOiJodHRwczovL2lzc3Vlci50bGQiLCJjbGFpbXMiOlsiZmFt - aWx5X25hbWUiLCJnaXZlbl9uYW1lIiwiZW1haWwiLCJhZ2UiXSwidHlwIjoiSlBUIiw - icHJvb2ZfandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiYWNiSVFpdU - 1zM2k4X3VzekVqSjJ0cFR0Uk00RVUzeXo5MVBINkNkSDJWMCIsInkiOiJfS2N5TGo5d - ldNcHRubUt0bTQ2R3FEejh3Zjc0STVMS2dybDJHekgzblNFIn0sInByZXNlbnRhdGlv - bl9qd2siOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJvQjFUUHJFX1FKSUw - 2MWZVT09LNURwS2dkOGoyemJaSnRxcElMRFRKWDZJIiwieSI6IjNKcW5ya3VjTG9ia2 - RSdU9xWlhPUDlNTWxiRnllbkZPTHlHbEctRlBBQ00ifSwiYWxnIjoiU1UtRVMyNTYif - Q", - "proof": "LJMiN6caEqShMJ5jPNts8OescqNq5vKSqkfAdSuGJA1GyJyyrfjkpAG0c - DJKZoUgomHu5MzYhTUsa0YRXVBnMB91RjonrnWVsakfXtfm2h7gHxA_8G1wkB09x09k - on2eK9gTv4iKw4GP6Rh02PEIAVAvnhtuiShMnPqVw1tCBdhweWzjyxJbG86J7Y8MDt2 - H9f5hhHIwmSLwXYzCbD37WmvUEQ2_6whgAYB5ugSQN3BjXEviCA__VX3lbhH1RVc27E - YkRHdRgGQwWNtuExKz7OmwH8oWizplEtjWJ5WIlJpee79gQ9HTa2QIOT9bUDvjjkkO- - jK_zuDjZwh5MkrcaQ", - "presentation": "eyJub25jZSI6InVURUIzNzFsMXB6V0psN2FmQjB3aTBIV1VOaz - FMZS1iQ29tRkx4YThLLXMifQ" -} -``` - -### Verification - -The verifier decodes the received JPT presentation and asserts the validity of the ZKP it contains, thus proving the -authenticity and integrity of the presented credential, without knowledge of any of the undisclosed fields and of the issuer signature. - - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/9_zkp.rs#L244-L257 -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/8_zkp.ts#L217-L225 -``` - - - - -## Full Example Code - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/1_advanced/9_zkp.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/1_advanced/8_zkp.ts -``` - - - diff --git a/docs/content/developer/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx b/docs/content/developer/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx deleted file mode 100644 index 78dad49a2b7..00000000000 --- a/docs/content/developer/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -sidebar_label: Create and Validate -description: Explain how a VP is created and verified -image: /img/identity/icon.png -tags: - - how-to - - identity ---- - -# Create and Validate Verifiable Presentations - -The IOTA Identity Framework enables holders to easily construct -[verifiable presentations](./../../explanations/verifiable-presentations.mdx). -As demonstrated in the [example](#example-code), -holders only need to pass in their credentials to create a JWT presentation. - -## Properties - -You can specify the following properties in a presentation: - -- [**ID**](https://www.w3.org/TR/vc-data-model/#identifiers): Optional URI identifier for the presentation. -- [**Context**](https://www.w3.org/TR/vc-data-model/#contexts): List of JSON-LD context URIs. Includes `"https://www.w3.org/2018/credentials/v1"` by default. -- [**Types**](https://www.w3.org/TR/vc-data-model/#types): List of types describing the presentation. Includes `"VerifiablePresentation"` by default. -- [**Credentials**](https://www.w3.org/TR/vc-data-model/#dfn-verifiable-credentials): List of verifiable credentials to present. -- [**Holder**](https://www.w3.org/TR/vc-data-model/#dfn-holders): Optional URI, typically a DID, of the entity that generated the presentation. -- [**Refresh Service**](https://www.w3.org/TR/vc-data-model/#refreshing): Optional link to a service where the recipient may refresh the included credentials. -- [**Terms of Use**](https://www.w3.org/TR/vc-data-model/#terms-of-use): Optional list of policies defining obligations, prohibitions, or permissions of the presentation recipient. - -Of the above, **only the list of credentials is required** when creating a presentation using the framework. -However, the holder property should be included to satisfy [subject-holder relationship](#subject-holder-relationship) checks during validation. - -After creation, the holder signs the verifiable presentation using a private key linked to one of the verification -methods in their DID Document and transmits it to a verifier for validation. - -## Creation and Validation - -A Verifiable Presentation can be issued as a JWT that provides data integrity, -and also proves the [DID](../../explanations/decentralized-identifiers.mdx) of the holder. - -:::note - -Verifiers should always send a challenge -to [mitigate replay attacks](./../../explanations/verifiable-presentations.mdx#security-considerations). -::: - - -The IOTA Identity Framework provides several options for verifiers to validate various sections of a verifiable presentation. -See the [example](#example-code) for a demonstration of how to validate a presentation. - -The framework checks: - -- **Semantic structure**: Ensures the presentation and its credentials adhere to the specification. -- **Presentation proof**: Verifies the presentation signature against the holder's DID document. -- **Credential proofs**: Verifies the credential signatures against the DID Documents of their respective issuers. - - -Currently, the following are **not** checked automatically: - -- **Data schemas**: Credentials that specify a [schema](https://www.w3.org/TR/vc-data-model/#data-schemas) property -should be examined to ensure conformance. -- **Fitness for purpose**: Whether the credentials in a presentation and the data within them are acceptable and -valid depends on the context in which they are used. Verifiers should ensure that the credential types, subjects, -and schemas sent by a holder match what was requested. -- **Issuer trustworthiness**: Verifiers must check that they trust the issuer on each individual credential in a -presentation. The framework only verifies that the issuer's signature on each credential is current and valid -against the given options. - -The default validation behavior may be modified by the following options. - -## Subject-Holder Relationship - -Specifies the expected relationship between the holder that signed the verifiable presentation and the subject -specified in each [verifiable credential](./../../explanations/verifiable-credentials.mdx). -This can be restricted by the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property, -which indicates that a verifiable credential must only be encapsulated into a verifiable presentation whose holder matches the credential subject. - -By default, the framework always enforces that the holder matches the subject. - -The following options are available to modify that behavior: - -- **`AlwaysSubject` (default)**: The holder DID that signed the presentation must match the [`credentialSubject` `id`](https://www.w3.org/TR/vc-data-model/#credential-subject) field in each of the attached credentials. This is the safest option which ensures holders may only present credentials that were directly issued to their DID. An error is thrown on a mismatch or if no subject `id` is present. -- **`SubjectOnNonTransferable`**: The holder DID must match the subject only for credentials where the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property is `true`. This is appropriate for accepting [bearer credentials](https://www.w3.org/TR/vc-data-model/#bearer-credentials) while still adhering to the specification. -- **`Any`**: The holder DID is not required to have any kind of relationship to any credential subject. This option performs no checks and ignores the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property. - -:::note - -See the [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships) -for further discussion on the different subject-holder relationships. - -::: - - -## Example Code - -The following code demonstrates how to use the IOTA Identity Framework end-to-end to create and sign a verifiable -presentation as a holder, serialize it to JSON for transmission, deserialize it on the receiving side as a verifier, -and finally validate it with various options. - - - - -```rust reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/examples/0_basic/6_create_vp.rs -``` - - - - -```ts reference -https://github.com/iotaledger/identity.rs/blob/v1.3.0/bindings/wasm/examples/src/0_basic/6_create_vp.ts -``` - - - diff --git a/docs/content/developer/iota-identity/welcome.mdx b/docs/content/developer/iota-identity/welcome.mdx deleted file mode 100644 index 41a91102fe3..00000000000 --- a/docs/content/developer/iota-identity/welcome.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -description: The most important concepts that developers will need to know to utilize IOTA Identity to its full potential. -image: /img/identity/icon.png -tags: - - reference - - identity ---- -# IOTA Identity Framework - -![IOTA Identity](/img/banner/banner_identity.svg) - -The IOTA Identity framework implements the most common standards and patterns for Decentralized Identity in both a DLT agnostic and `iota` method-specific manner. -It is designed to work for Identity for [People](#identity-for-people), [Organizations](#identity-for-organizations), -[Things, and Objects](#identity-for-things) acting as a unifying layer of trust between everyone and everything. - -## Introduction to Decentralized Identity - -Decentralized or Self-Sovereign Identity (SSI) gives individuals full control over their online identity, -offering a remedy for database breaches, lack of digital trust, and stringent privacy laws like GDPR. -Digital identity bridges the gap between online pseudonyms and real-world personas, enabling true verifiable identities. This gives individuals the power to choose which data to share and with whom. - - - -### Identity for People - -:::info Privacy - -IOTA Identity builds a new internet, without usernames, passwords, endless repeated forums, or uncontrolled data harvesting. - -::: - -Information about anyone's life is spread across many locations. Most people have numerous unorganized important documents at home, hundreds of online accounts, and many more online footprints. Through statistical predictive analysis, computer programs can harvest unverified online information sources and create a reasonably accurate profile about our lives. These profiles are accurate enough for targeted advertising and personalized content but lack the proof and trust for them to be used in business. This results in an antiquated customer experience where we have to submit our age and address for every purchase we make and every account we create. It also inhibits our ability to do many online tasks like requesting and extending licenses or taking out a mortgage. - -Self-Sovereign Identity is about returning autonomy and privacy to the individual, while also improving our online experience. Some movements focus on data privacy, preventing companies from using our information altogether, but with the IOTA Identity framework you control which part of the information you want to reveal. The user can create a single online profile containing all our personal information. They can decide who they share what information with, and a verifier checks if the information is correct, making the data trustworthy. This moves their online profile from a statistical estimation by corporate entities to an accurate and verifiable profile under their own control. - -IOTA Identity allows a new internet without usernames, passwords, endlessly repeated forms or data harvesting. Users have ultimate control and can choose to supply service providers with their personal data, who in return provide personalized experiences. Data will still flow, and perhaps even more than before, but it will always be in the individual's interest instead of a corporation's. People will gain additional benefits in sharing their data, either in monetary value or improved customer experience. This system is impossible in non-neutral environments such as permissioned or fee-based ledgers. - -Governmental mechanisms for building _digital identities_ are currently being established throughout Europe and Asia, with demand increasing around the globe. However, they are managed by single entities and restricted to the governments that created them. By decentralizing a framework for these standards to adapt to, we have a system for intergovernmental verification of individuals and devices. A person’s digital identification will be transferable across borders like a passport. However, it will no longer require the trust of the issuing government due to the digital trust established by the open and auditable system. - -### Identity for Organizations - -:::info GDPR - -IOTA Identity allows organizations to comply with GDPR in a cost-efficient and privacy-enabling manner - -::: - -Corporations are associated with greed and abuse of power. This reputation stems from the role some have chosen to take within society. Corporations are trusted with our data, but often do not act responsibly; vulnerability, fix, patch, repeat. In software and systems, we have seen this cycle repeat. Headlines on data leaks are now an ever-present feature in the news. - -IOTA Identity presents an opportunity for companies to embrace a new role in the ecosystem. Traditional approaches do not provide cost-efficient solutions to new legislation like GDPR. IOTA Identity enables organizations to change their processes to comply with the new regulations in a cost-efficient and privacy-enabling manner. Features of “Data Protection and Privacy by Design” shift responsibility over Personal Identifiable Information (PII) from organization to customer, and organizations no longer need to store that data. The relationship between customer and organization is also tightened as communication via a third-party Identity provider like Google or Facebook is no longer needed. - -Due to Know-Your-Customer (KYC) and Anti-Money Laundering (AML) obligations, companies can be certain who their customers are. These services also provide unique insight into their customers’ data. These insights can be combined and translated into verifiable credentials, providing a new “Trust Anchor” service with the potential for new business models. KYC and AML credentials would return the autonomy of personal data to the customer. Once companies accept other companies' KYC and AML credentials, the enrollment time for new customers is significantly reduced, as are the costs. With the personal data secured by the customer, companies can afford to store less data in their databases, reducing risk and responsibility and fulfilling the goals of legislation such as GDPR. - -Organizations that have their own decentralized identities can also combat fraud and increase control over their online brand. Companies can sign invoices and agreements using their decentralized identities. While interacting with the customers, they will also be able to reliably identify themselves. - -### Identity for Things - -:::info TRUST - -IOTA Identity adds the missing key ingredient for the "Economy of Things": Trust. - -::: - -With Identity of Things (IDoT), devices are provided with a unique global identity that are able to prove many attributes, including their capabilities, specifications, and authenticity. People, organizations, and other devices will only pay for devices that can prove their ability to fulfill the required task. This basis of trust prevents fraudulent activity. Additionally, using the IOTA ledger, the task's progress can be immutably logged. Combining the IOTA protocol and the IOTA Identity framework, we can automate the entire interaction between all parties without requiring predefined trust. The [Industry Marketplace](https://industry.iota.org/) provides a perfect example of how this framework and level of autonomy work. - -There is a growth in applications that generate Digital Twins for physical devices or objects, such as the Asset Administration Shell (AAS) developed for our Industry Marketplace. Digital twins are online profiles representing a device or object. They provide a virtual state that mirrors reality by emulating the device or object’s physical state through data input sources like sensors. A digital twin is often used to monitor states and execute actions based on the information. Digital twins are only rarely shared outside the associated application and organization due to the complexities in sharing and matching profiles. However, empowered with a digital identity, digital twin sharing would become possible. Once data is verifiable and trusted, digital twins can form the basis for the digital representation of physical devices and objects. This allows other identities to interact with them automatically and provide services such as predictive maintenance. - -Security is a major barrier to advancing technologies that use IoT. Whether it is the smart devices in our homes or at a larger scale, the critical infrastructure of organizations and cities, security must be at the core. It is central to any globally unifying identity solution. By integrating advanced research in cryptography and digital ledgers and combining it with a scalable access and management system, security will become a core functionality of the systems we build. By using scalable device DIDs, integrating verification and reputation schemes, and allowing for transparent tamper-proof accountability, we begin to understand how we can future-proof the security of our systems, allowing us to start trusting the process and not the patch. - -### One Framework. Any Identity - -The IOTA Identity framework serves as a ubiquitous layer of trust for the internet. Whether it's people, organizations, or things, the framework enables the creation of digital identities, fosters trust-building through verifiable credentials, and ensures seamless interaction among different entities. - -### Why IOTA? - -IOTA stands apart as a scalable, feeless Distributed Ledger Technology (DLT), suitable for a universal identity solution. Some features of IOTA include: - -* **Cost-effectiveness**: Usually, minting decentralized identities costs fees. IOTA Identity has redeemable and predictable deposits but no fees. -* **High availability**: Identities are always available on all network nodes - for holders, issuers, and verifiers. -* **Security**: Write access to identities is secured through multi-level control structures with key rotation capabilities, allowing for backup access and recoverability. -* **Integrity**: Updates go through the same mechanisms that secure the IOTA network, guaranteeing consistent state and history of all identities. diff --git a/docs/content/developer/iota-identity/workflow.mdx b/docs/content/developer/iota-identity/workflow.mdx deleted file mode 100644 index a52b79b81e1..00000000000 --- a/docs/content/developer/iota-identity/workflow.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Identity.rs workflow -sidebar_label: Workflow -description: Learn about the software development process of the IOTA Identity repository. -image: /img/identity/icon.png -tags: - - reference - - identity ---- - -# Identity Workflow - -In this article you will learn about the software development process for the IOTA Identity repository as well as key terms, functions, and the overall operability of the workflow components. - -## Issues - -Issues are opened when a certain task or problem is noted but cannot immediately be fixed. Issues may contain bug reports, requests, or larger topics. Please use the correct GitHub issue template for your issue type. Only IOTA Foundation members should use the task templates flagged for maintainers. You should make sure to [label](#issue-labels) the issue correctly. As a contributor, you may also add issues to a certain [project](https://github.com/iotaledger/identity.rs/projects/). - -## Git - -### Pull Requests - -New branches should be pushed to the GitHub repository as soon as possible, making them public to all contributors. In addition, a pull request (PR) should be opened in draft status, describing the goals and any requirements of the changes. To generate good [changelogs](#changelog), a PR title must be written in a way that is suitable as a changelog entry while the PR must be [labeled](#pr-labels) correctly. - -Any code written should frequently be committed and pushed back to the GitHub branch. This acts as both a back-up mechanism and provides transparency towards other contributors and the community. You should also pull from the origin branch of the PR regularly to prevent merge conflicts. - -Other contributors are encouraged to provide feedback on a PR during its development. A PR should be flagged as 'ready for review' once the PR has implemented all changes and no further commits are planned by the main contributors. The repository requires a review to be provided by at least one (other) developer in the team that works in the same language or has knowledge of the work before it can be merged. For larger PRs, the review of two maintainers is recommended. - -Once a PR is approved, the preferred method is "squash-and-merge" for non-epic branches to keep the destination branch clean and allow for many small commits while work is in-progress. Epic branches must instead be merged with the merge commits of included PRs intact, so the [changelog generator](#changelog) can detect included changes. Once merged in, the source branch may be deleted. - -### Branches - -IOTA Identity always has two permanent branches: `main` and `dev`. Both branches are protected and disallow direct commits; the only changes allowed are from pull requests approved and merged by maintainers. - -#### [Main](https://github.com/iotaledger/identity.rs/tree/main) (main) - -The `main` branch contains a stable version of the code that is released towards package managers such as `crates.io` and `npm`. This branch only accepts PRs that merge from `release` or `hotfix` branches. - -#### [Dev](https://github.com/iotaledger/identity.rs) (dev) - -The `dev` branch contains a frequently updated version of the code that is released towards package managers under a development flag. These releases may contain breaking changes without a strong notice towards developers using them. While the `dev` branch may get frequent updates, it may not contain unfinished features. Any large, multi-PR feature should be committed to a long-lived `epic` branch created specifically for that feature. - -### Work Branches - -These are branches that developers work on directly. Their names should be prefixed appropriately with one of the following categories. For example, a PR fixing a null pointer bug in the Wasm bindings might be created from a branch called `fix/client-non-null`. - -#### Feature (feat/, doc/, chore/, fix/) - -Singular PR contributions should create either a `feat`, `doc`, `chore`, or `fix` branch, depending on the type of changes. These may be branched from either the `dev` branch or an `epic` branch. If the number of lines of code are going to be relatively small and the work completed in a single PR, the branch should be created from `dev` and merged back into `dev` once completed. Otherwise, the branches should be created from their associated `epic` branch and be merged back into the same `epic` branch. - -- `feat` branches should contain changes to the code that expand or modify functionality. They should also include updates to the documentation and examples related to the feature, though `doc` branches may be used to catch up on documenting a feature. -- `doc` branches contain changes to code documentation or the wiki. These PRs should be kept relatively small to avoid burdening a reviewer with too many documentation updates at once. For example, during a documentation catch-up, we will have a branch or PR per documentation page. -- `chore` branches are short-lived branches that contain no significant features or functionality changes, but rather smaller fixes such as typos, code fixes, minor refactors, and CI changes. -- `fix` branches correct bugs such as compilation errors or where existing features do not behave as expected, generally without introducing any new functionality or breaking changes. - -We recommend integrating `dev` or `epic` regularly, depending on where the branch started, to reduce the possibility and potential size of merge conflicts. - -#### Epic (epic/) - -Long-lived `epic` branches should be created as soon as a feature is expected to require more than one PR. The `epic` branch should be branched from `dev` and should only accept merges that are related to the feature being developed. A PR should be opened as soon as the branch is created to publicly notify contributors about the development, the goals and requirements of the feature, and the existence of the branch. It is recommended you integrate `dev` often to reduce the possibility and potential size of merge conflicts. Epic branches must not be squash-merged, otherwise the [changelog generator](#changelog) will not detect its constituent PRs. - -### Semantic Versioning - -Semantic Versioning (SemVer) describes a methodology for versioning of software to convey meaning and guarantees through the version string. A typical version string looks like `2.3.1`, where `2` is called the major version, `3` the minor version and `1` the patch or bugfix version. - -The central idea is that every part of the version string conveys meaning. A major change introduces behavior that is incompatible with previous versions of the software, while a minor change adds backwards-compatible functionality and a patch simply fixes a problem. So just by looking at the version string, an implementer will understand the effort needed to integrate a new version. - -For more detailed information and an overview of advanced features, see [Semantic Versioning 2.0.0](https://semver.org/). Though this is not to be confused with [Sentimental Versioning](http://sentimentalversioning.org/). - -### Changelog - -A changelog is a file describing a software project for humans to grasp the type and content of changes from version to version. Changelogs are closely related to the versioning of software, since individual changes are grouped into versions that are, in our case, referenced by a [SemVer string](#semantic-versioning). We generally follow the recommendations from [keepachangelog](https://keepachangelog.com/en/1.0.0/). The changelog in this project is generated from the titles and [labels](#pr-labels) of [pull requests](#pull-requests). - -#### PR labels - -Labels are used to categorize changes in [pull requests](#pull-requests). Adding a label will include the labeled [PR](#pull-requests) title in the related section of the generated [changelog](#changelog). - -Changelogs are generated for the core Rust library and each binding separately. To attach a PR to a specific changelog, use the following labels: - -##### `Rust` - -This includes the PR in the core Rust library changelog. - -##### `Wasm` - -This includes the PR in the WASM bindings changelog. - -It is also necessary to add an appropriate label for the type of change in the PR. The following labels determine in which section a PR title will appear: - -##### Changed - -Maps to the major version of [Semantic Versioning](#semantic-versioning). -labels: `Breaking change` - -##### Added - -Maps to the minor version of [Semantic Versioning](#semantic-versioning). -labels: `Added` - -##### Patch - -Maps to the patch version of [Semantic Versioning](#semantic-versioning). -labels: `Patch` - -##### Deprecated - -Marks features that will be removed in the feature. No special version consideration should apply here, since the feature did not change yet. -labels: `Deprecated` - -##### Removed - -Marks features as being removed. Typically the features should have been deprecated in the previous version. This maps to the major version of [Semantic Versioning](#semantic-versioning). -labels: `Removed` - -##### Excluded tags - -Marks changes that should not be part of the changelog. This should only be used for documentation and rare exceptions. -labels: `Documentation`, `No changelog` - -Please note that a PR can only be listed in one section of a changelog. So attaching the labels `Rust` `Added` `Patch` to a PR, for example, is invalid because `Added` and `Patch` conflict. - -##### Release summary - -To attach a release summary to a version in the changelog, an issue with the label `release-summary` must be created. Create a GitHub milestone matching the version you want to describe and attach it to the issue. The issue can be closed immediately. The text of the issue will be included in the changelog as the release summary. - -### Issue Labels - -The following labels are used to categorize issues but do not have any effect on changelogs: `Request`, `Enhancement`, `Bug`, `Chore`, `Dependencies`, `Help wanted`, `Duplicate`, `Wontfix`. - -## Release - -With the release process, we can deliver versions of our software to the community. We use sensible automation where it helps to remove tedium. However, some steps that require active decision-making remain manual. - -The final list of changes from the [changelog](#changelog) informs the version of the release. If at least one change mapping to a major version is included, the major version needs to be incremented. In that case, the minor and patch versions are set to `0`. If there are no changes related to a major version, but changes related to a minor version are present, the minor version needs to be incremented while the patch version is set to `0`. Otherwise, only the patch version is incremented. Determining the version of the release is the responsibility of the person performing the release. - -The determined version of the release is used to create the hotfix or [release](#release) branch. For example, a major release from the previous version `v2.3.1` will create the `release/v3.0.0` branch. - -Notice the `v` in front of the version. We [tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) all release in git in the form of `vMAJOR.MINOR.PATCH`. For bindings, we prefix the tag with the binding name, so a tag for Wasm would look like `wasm-v1.2.3`. Bindings and the core Rust library are versioned and released independently. - -Additionally, we may release `dev` versions separately for both bindings and the core Rust library. These releases are meant as a preview of upcoming versions. For example, if the current version is `1.2.3` with the tag `v1.2.3`, we may release `v1.3.0-dev.1` which is then superseded by the actual `1.3.0` release. - -You should follow these steps to create a release: - -1. Ensure all the changes you want to release are on the `dev` branch. -2. Select the appropriate GitHub Actions workflow, e.g. `Rust Create Release PR`. - 2.1. Decide if you want to create a `dev` or `main` release. - 2.2. Determine the next version string. - 2.3. Run the workflow. The workflow will create a PR from `dev` targeting `dev` with release related changes. -3. Review the PR. - 3.1. The PR will update the changelog, check that it has all expected entries in the appropriate sections and the determined version matches the changelog according to [SemVer](#semantic-versioning). - 3.2. The PR will update project version strings, ensure these are correct and match the expected version. - 3.3. Refer to [Troubleshooting](#troubleshooting) if anything is incorrect. -4. Merge the PR. - 4.1. On merging to `dev`, an automatic workflow is triggered that builds and publishes artifacts to the appropriate package manager (`crates.io` for Rust, `npm` for the WASM bindings), and creates a GitHub Release (only for `main` version releases of the core Rust library). -5. For `main` version releases, merge the `dev` branch into the `main` branch. - -### Troubleshooting - -#### The changelog entries have the wrong description in the release PR - -Update the titles of the relevant PRs, then re-run the workflow with the same parameters. The release PR will be updated with the new changelog. - -#### The changelog in the release PR is missing entries, has unrelated entries, or entries in the wrong section - -Fix the [labels](#pr-labels) on the relevant PRs, then re-run the workflow with the same parameters. The release PR will be updated with the new changelog. - -#### The release description in the release PR is missing or wrong - -Fix the issue description, milestone, and label according to the [release summaries guide](#release-summary) and re-run the workflow with the same parameters. The release PR will be updated with the new changelog. - -#### Features or code are missing from the release - -Merge the code into the `dev` branch, then re-run the workflow with the same parameters. The release PR will be updated with the changes. - -#### I want to abort the release for any reason - -Close the PR. You can reopen it later. // TODO: can I just re-run the workflow? Maybe that needs an "I want to resume an aborted release" section? diff --git a/docs/content/developer/iota-move-ctf/challenge_1.mdx b/docs/content/developer/iota-move-ctf/challenge_1.mdx index ab3b6f588b4..a8493b5bc13 100644 --- a/docs/content/developer/iota-move-ctf/challenge_1.mdx +++ b/docs/content/developer/iota-move-ctf/challenge_1.mdx @@ -29,8 +29,8 @@ Package: 0xce9b1471301ffaf1453297cca008a68ce851b6a9ba9ab241c357c346177903f3 In this challenge, you must use the IOTA CLI to interact with the blockchain. This set of articles will help you set up your environment and call a deployed contract: - [Installing IOTA CLI](../getting-started/install-iota.mdx) -- [Connecting to Alphanet](../getting-started/connect.mdx) -- [Get Iota coins](../getting-started/get-coins.mdx) +- [Connecting an IOTA Network](../getting-started/connect.mdx) +- [Get IOTA coins](../getting-started/get-coins.mdx) - [Accessing Your Package](../getting-started/publish.mdx#accessing-your-package) diff --git a/docs/content/developer/iota-move-ctf/challenge_4.mdx b/docs/content/developer/iota-move-ctf/challenge_4.mdx index 2442c2b30a8..20a4656ed6a 100644 --- a/docs/content/developer/iota-move-ctf/challenge_4.mdx +++ b/docs/content/developer/iota-move-ctf/challenge_4.mdx @@ -35,7 +35,7 @@ This challnege can be solved with IOTA PTBs, which will also help you in further - [Coin Standard](../standards/coin.mdx) - [Object Model](../iota-101/objects/object-model.mdx) -- [Programmable Transaction Blocks](../iota-101/transactions/ptb/working-with-ptbs.mdx) +- [Programmable Transaction Blocks](../iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx) diff --git a/docs/content/developer/iota-move-ctf/challenge_7.mdx b/docs/content/developer/iota-move-ctf/challenge_7.mdx index 2e534975317..586a34c327f 100644 --- a/docs/content/developer/iota-move-ctf/challenge_7.mdx +++ b/docs/content/developer/iota-move-ctf/challenge_7.mdx @@ -26,7 +26,7 @@ Package: 0x202d65a2b1d2de4ba90e9eeb51ef4e16fafdaaa5c8b1dc3cbd8a935e5eb4d25c This challenge will introduce you to the PTB standard and how to use the Move CLI to interact with it. You should be familiar with the PTB standard and how to use the Move CLI to call the `ptb` function. -- [PTB Standard](../iota-101/transactions/ptb/working-with-ptbs.mdx) +- [PTB Standard](../iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx) - [IOTA CLI reference](../../references/cli/ptb.mdx) diff --git a/docs/content/developer/iota-move-ctf/challenge_8.mdx b/docs/content/developer/iota-move-ctf/challenge_8.mdx index 66cf831c1b7..86e3ddfded1 100644 --- a/docs/content/developer/iota-move-ctf/challenge_8.mdx +++ b/docs/content/developer/iota-move-ctf/challenge_8.mdx @@ -7,7 +7,7 @@ import ChallengeVerifier from '@site/src/components/CTF/ctf-verifier'; # Challenge 8: Flash! In this challenge, you will explore a decentralized exchange (DEX) with a critical flaw you can exploit to capture the flag. This exchange operates with two tokens—CTFA and CTFB—and features a vault that allows users to take flash loans. Your objective is to manipulate the token balances effectively to obtain the flag by using the vulnerabilities in the DEX's flash loan mechanism. -To solve this challenge, you will have to have a deep understanding of [programmable transaction blocks (PTBs)](../iota-101/transactions/ptb/prog-txn-blocks.mdx) and how to build them using the [TS SDK](../iota-101/transactions/ptb/building-ptb.mdx) or the [CLI](../../references/cli/ptb.mdx). +To solve this challenge, you will have to have a deep understanding of [programmable transaction blocks (PTBs)](../iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx) and how to build them using the [TS SDK](../iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx) or the [CLI](../../references/cli/ptb.mdx). ## Deployed Contract Addresses: ``` @@ -40,9 +40,9 @@ This challenge will test your understanding of the Object Model, the Coin Standa - [Coin Standard](../standards/coin.mdx) - [Object Model](../iota-101/objects/object-model.mdx) -- [Programmable Transaction Blocks](../iota-101/transactions/ptb/working-with-ptbs.mdx) +- [Programmable Transaction Blocks](../iota-101/transactions/ptb/programmable-transaction-blocks-overview.mdx) - [IOTA CLI reference](../../references/cli/ptb.mdx) -- [IOTA TS SDK reference](../iota-101/transactions/ptb/building-ptb.mdx) +- [IOTA TS SDK reference](../iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx) diff --git a/docs/content/developer/iota-move-ctf/introduction.mdx b/docs/content/developer/iota-move-ctf/introduction.mdx index ab5d39c2c13..1b33e9f4639 100644 --- a/docs/content/developer/iota-move-ctf/introduction.mdx +++ b/docs/content/developer/iota-move-ctf/introduction.mdx @@ -1,6 +1,6 @@ # Move on IOTA Capture The Flag (CTF) -The **Move on IOTA Capture The Flag (CTF)** is a unique learning experience designed to help developers master the Move programming language by solving a series of challenges. These challenges simulate real-world scenarios that require participants to find and exploit vulnerabilities, optimize contract logic, and interact with IOTA’s alphanet. Whether you're new to Move or experienced with smart contract development, this CTF is an excellent way to sharpen your skills. +The **Move on IOTA Capture The Flag (CTF)** is a unique learning experience designed to help developers master the Move programming language by solving a series of challenges. These challenges simulate real-world scenarios that require participants to find and exploit vulnerabilities, optimize contract logic, and interact with IOTA’s Testnet. Whether you're new to Move or experienced with smart contract development, this CTF is an excellent way to sharpen your skills. This CTF features **eight challenges**, each built on Move, a language optimized for safety and scalability in decentralized applications. Throughout the event, participants are encouraged to explore Move’s features, including its resource-based design and transaction scripting model, in a competitive environment. However, bear in mind that the provided demonstration contracts may intentionally include distractions, poor design, and obscure naming conventions to increase difficulty. @@ -17,10 +17,10 @@ Upon successfully completing a challenge, you will receive a "flag," which is a ## Setup -The CTF contract are already deployed on the IOTA alphanet. To get started, you need to install the IOTA CLI tool and connect to the alphanet. If you haven't done so already, follow these steps: +The CTF contract are already deployed on the IOTA Testnet. To get started, you need to install the IOTA CLI tool and connect to the Testnet. If you haven't done so already, follow these steps: 1. [Install the IOTA CLI Tool](../getting-started/install-iota.mdx) -2. [Connect to alphanet](../getting-started/connect.mdx) +2. [Connect to an IOTA Network](../getting-started/connect.mdx) 3. [Create and Address](../getting-started/get-address.mdx) 4. [Fund Your Address](../getting-started/get-coins.mdx) 5. Get the flags! diff --git a/docs/content/developer/network-overview.mdx b/docs/content/developer/network-overview.mdx index 8dfb43e3403..aff8a1c6402 100644 --- a/docs/content/developer/network-overview.mdx +++ b/docs/content/developer/network-overview.mdx @@ -1,15 +1,16 @@ --- description: This page gives an overview around the different networks available in the IOTA ecosystem. -tags: [getting-started, devnet, testnet, mainnet] +tags: [getting-started, devnet, testnet] --- +import MainnetRunsStardust from '../_snippets/mainnet-runs-stardust.mdx'; +import NetworkInfo from "@site/src/components/NetworkInfo/index.tsx"; +import { Networks } from '@site/src/components/constant.tsx'; # Network Overview This pages gives an overview around the different networks available in the IOTA ecosystem. -import NetworkInfo from "@site/src/components/NetworkInfo/index.tsx"; -import { Networks } from '@site/src/components/constant.tsx'; :::info In general, all endpoints are load-balanced but also subject to rate limiting. If you are developing a production-level application for IOTA, consider [setting up your own infrastructure](/developer/getting-started/local-network) to have more control and avoid any limits. @@ -17,11 +18,7 @@ In general, all endpoints are load-balanced but also subject to rate limiting. I ## IOTA Mainnet -:::note -The Mainnet and Devnet have not yet gone live. -::: - - + ## IOTA Testnet diff --git a/docs/content/developer/standards/display.mdx b/docs/content/developer/standards/display.mdx index 50d701df610..82db8ce3784 100644 --- a/docs/content/developer/standards/display.mdx +++ b/docs/content/developer/standards/display.mdx @@ -8,7 +8,7 @@ import questions from '/json/developer/standards/display.json'; The IOTA Object Display standard is a template engine that enables on-chain management of off-chain representation (display) for a type. With it, you can substitute data for an object into a template string. The standard doesn’t limit the fields you can set. You can use the `{property}` syntax to access all object properties, and then insert them as a part of the template string. -Use a `Publisher` object that you own to set `iota::display` for a type. For more information about `Publisher` objects, see [Publisher](https://examples.iota.io/basics/publisher.html) topic in *IOTA Move by Example*. +Use a `Publisher` object that you own to set `iota::display` for a type. In IOTA Move, `Display` represents an object that specifies a set of named templates for the type `T`. For example, for a type `0x2::hero::Hero` the display syntax is: `Display<0x2::hero::Hero>`. diff --git a/docs/content/developer/stardust/claiming.mdx b/docs/content/developer/stardust/claiming.mdx index f95616c4afe..471298bb3e1 100644 --- a/docs/content/developer/stardust/claiming.mdx +++ b/docs/content/developer/stardust/claiming.mdx @@ -7,7 +7,7 @@ import MigrationWarning from '../../_snippets/migration-warning.mdx'; -As detailed in the [Stardust Move Models](move-models.mdx), Stardust assets are represented as Move objects within the ledger. Claiming these assets involves enabling original owners to utilize a [Programmable Transaction Block](../iota-101/transactions/ptb/prog-txn-blocks.mdx) to "unlock" assets such as IOTA, custom [`Coin`](../../references/framework/iota-framework/coin.mdx#resource-coin)s, or even `Alias` and `Nft` objects. +As detailed in the [Stardust Move Models](move-models.mdx), Stardust assets are represented as Move objects within the ledger. Claiming these assets involves enabling original owners to utilize a [Programmable Transaction Block](../iota-101/transactions/ptb/programmable-transaction-blocks) to "unlock" assets such as IOTA, custom [`Coin`](../../references/framework/iota-framework/coin.mdx#resource-coin)s, or even `Alias` and `Nft` objects. This process takes advantage of Move's unique features to ensure that assets are transferred and unlocked securely and efficiently to their rightful owners. @@ -21,7 +21,7 @@ Below, you will find one or more examples of claiming each Output type. ## Examples of Stardust Asset Claim Transactions Here are some examples of transactions for claiming Stardust assets. -Different commands in a [PTB](../iota-101/transactions/ptb/prog-txn-blocks.mdx) are used depending on the claiming scenario, +Different commands in a [PTB](../iota-101/transactions/ptb/programmable-transaction-blocks) are used depending on the claiming scenario, which varies based on the Stardust Output type and composition. ### Basic Output diff --git a/docs/content/developer/stardust/claiming/alias.mdx b/docs/content/developer/stardust/claiming/alias.mdx index 6145a9e8968..7b248a77505 100644 --- a/docs/content/developer/stardust/claiming/alias.mdx +++ b/docs/content/developer/stardust/claiming/alias.mdx @@ -59,7 +59,7 @@ Next, check the native tokens that might be held by this output. A [`Bag`](../.. ### 3. Create the PTB -Finally, create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-ptb.mdx) +Finally, create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx) using the `alias_output_object_ref` as input along with the native token keys. An `AliasOutput` differs from an `NftOutput` or a `BasicOutput` because it contains the `Alias` object. The main purpose of claiming is to extract the `Alias` object from the `AliasOutput`. diff --git a/docs/content/developer/stardust/claiming/nft.mdx b/docs/content/developer/stardust/claiming/nft.mdx index c71927bc4b9..ae33a5c6d54 100644 --- a/docs/content/developer/stardust/claiming/nft.mdx +++ b/docs/content/developer/stardust/claiming/nft.mdx @@ -66,7 +66,7 @@ representing the [`OTW`](../../iota-101/move-overview/one-time-witness.mdx) used ### 3. Create the PTB Finally, -you can create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-ptb.mdx) using the `nft_output` as an input, +you can create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx) using the `nft_output` as an input, along with the `Bag` keys to iterate over the extracted native tokens. The primary goal of this process is to extract the `Nft` object from the `NftOutput`. diff --git a/docs/content/developer/stardust/claiming/self-sponsor.mdx b/docs/content/developer/stardust/claiming/self-sponsor.mdx index 9b29c875c8b..2d473fb1141 100644 --- a/docs/content/developer/stardust/claiming/self-sponsor.mdx +++ b/docs/content/developer/stardust/claiming/self-sponsor.mdx @@ -36,7 +36,7 @@ Use the IOTA `coin_type` to derive the sponsor and sender addresses. ### 2. Create the PTB for Claiming -Next, create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-ptb.mdx) +Next, create a [Programmable Transaction Block (PTB)](../../iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.mdx) to claim a `BasicOutput` owned by the derived Iota address. This process is similar to the one outlined in the [Basic Output](basic.mdx) guide. diff --git a/docs/content/operator/iota-evm/how-tos/chain-management.md b/docs/content/operator/iota-evm/how-tos/chain-management.md deleted file mode 100644 index 8df49333c90..00000000000 --- a/docs/content/operator/iota-evm/how-tos/chain-management.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -description: 'How to manage a chain using the Grafana dashboard, a client to receive published events, logging, and -validators.' -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - isc ---- - -# Manage a Chain - -## Monitoring - -You can view the chain state using the dashboard (`/wasp/dashboard` when using `node-docker-setup`). - -## Manage Chain Configuration and Validators - -You can manage the chain configuration and committee of validators by interacting with -the [Governance contract](/isc/../../references/iota-evm/core-contracts/governance). - -The “Chain Owner” is the only one who can perform administrative tasks. - -### Change Chain Ownership - -To change the chain ownership, the current “Chain Owner” must call `delegateChainOwnership` specifying the `agentID` of -the next owner. The next owner must call `claimChainOwnership` to finalize the process. - -### Change Access Nodes - -For new access nodes to join the network, they need to: - -- Be added as a trusted peer to at least 1 of the existing nodes. -- Be added by the administrator to the list of access nodes by calling `changeAccessNodes`. There is a helper in - wasp-cli to do so: - -```shell -wasp-cli chain gov-change-access-nodes accept -``` - -After this, new nodes should be able to sync the state and execute view queries (call view entry points). - -You can remove an access node by calling `changeAccessNodes`. - -Alternatively, you can add "non-permissioned" access nodes without the signature from the chain owner to add any node as an "access node". -You can do this by using the following command: - -```shell -wasp-cli chain access-nodes -``` - -This node won't be "officially" recognized by the committee but will still be able to sync the state and provide all regular functionality. - -### Change the Set of Validators - -You can do this in different ways, depending on who controls the [governor address](https://github.com/iotaledger/tips/blob/main/tips/TIP-0018/tip-0018.md#alias-output) -from the alias output of the chain. - -- If the chain governor address is the chain committee, you can perform the rotation by calling - `rotateStateController` after adding the next state controller via `addAllowedStateControllerAddress`. -- If the chain governor address is a regular user wallet that you control, you can issue the rotation transaction using wasp-cli: - -```shell -wasp-cli chain rotate -``` - -Or: - -```shell -wasp-cli chain rotate-with-dkg --peers=<...> -``` diff --git a/docs/content/operator/iota-evm/how-tos/running-a-node.md b/docs/content/operator/iota-evm/how-tos/running-a-node.md deleted file mode 100644 index 83dcbf47aae..00000000000 --- a/docs/content/operator/iota-evm/how-tos/running-a-node.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: How to run a node. Requirements, configuration parameters, dashboard configuration, and tests. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - isc ---- - -# Running a Node - -As Wasp is an INX plugin, you must run the wasp node alongside your _hornet node_. You can use the simple docker-compose setup to do so. - -## Recommended Hardware Requirements - -We recommend that you run the docker image on a server with: - -- **CPU**: 8 core. -- **RAM**: 16 GB. -- **Disk space**: ~ 250 GB SSD, depending on your pruning configuration. - -## Set Up - -Clone and follow the instructions on the [node-docker-setup repo](https://github.com/iotaledger/node-docker-setup). - -:::note -This is aimed at production-ready deployment. If you're looking to spawn a local node for testing/development, please see the [local-setup](https://github.com/iotaledger/wasp/tree/develop/tools/local-setup) -::: diff --git a/docs/content/operator/iota-evm/how-tos/running-an-access-node.mdx b/docs/content/operator/iota-evm/how-tos/running-an-access-node.mdx deleted file mode 100644 index 26f61ecd734..00000000000 --- a/docs/content/operator/iota-evm/how-tos/running-an-access-node.mdx +++ /dev/null @@ -1,279 +0,0 @@ ---- -description: How to setup an access node. -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - isc ---- - -# Running an ISC Access Node - -## Set Up Wasp and Hornet - -First, you should [set up a Wasp Node](./running-a-node.md) but don't run `docker compose up` to start the docker containers yet. Continue with the following steps first: - -### Download the Chain State - -You can configure how much of the state you wish to keep on your node by uncommenting and changing the `WASP_PRUNING_MIN_STATES_TO_KEEP` environment variable in the node-docker-setup. The default value is 10000 blocks. - -### Run a Non-archive Node - -If you wish to run a non-archive node (only keep the last N blocks), you must uncomment `WASP_SNAPSHOT_NETWORK_PATHS`. - -### Run An Archive Node - -If you wish to have a full archive node, you need to set `WASP_PRUNING_MIN_STATES_TO_KEEP` to `0` and comment `WASP_SNAPSHOT_NETWORK_PATHS` out. - -You can then download the historical state using the following command (this will take a while): - - - - - -```sh -wget https://files.stardust-mainnet.iotaledger.net/dbs/wasp/latest-wasp_chains_wal.tgz -O - | tar xzv -C data/wasp -``` - - - - -```sh -wget https://files.iota-testnet.iotaledger.net/dbs/wasp/latest-wasp_chains_wal.tgz -O - | tar xzv -C data/wasp -``` - - - - -```sh -wget https://files.shimmer.network/dbs/wasp/latest-wasp_chains_wal.tgz -O - | tar xzv -C data/wasp -``` - - - - -```sh -wget https://files.testnet.shimmer.network/dbs/wasp/latest-wasp_chains_wal.tgz -O - | tar xzv -C data/wasp -``` - - - - -:::note Disk Space - -Operating as a full archive node requires a lot of disk space. We recommend at least 500Gb of free space to operate without issues - -::: - -### Run Everything - -```sh -docker compose up -d -``` - -It will take a few minutes until the hornet node is synced. - -You can check the sync status by following the logs with `docker logs -f hornet`, or in the web dashboard. - -## Use Wasp-Cli to configure the chain and add peers -### Download Wasp-Cli - -You can download a Wasp-Cli that matches your Wasp version from the [Wasp releases](https://github.com/iotaledger/wasp/releases). -You can use a commands like the following to for example download version 1.1.0: - -```sh -curl -sL https://github.com/iotaledger/wasp/releases/download/v1.1.0/wasp-cli_1.1.0_Linux_x86_64.tar.gz | tar xzv -``` - -Change directory into the newly-downloaded `wasp-cli` directory: - -```sh -cd wasp-cli_1.1.0_Linux_x86_64/ -``` - -### Set the L1 API Address - -Set the L1 API address. You can set it to what you configured as `NODE_HOST` in the `.env` file - -:::tip HTTP - -If you use the http setup, don't forget to replace `https` with `http` - -::: - -```sh -./wasp-cli set l1.apiaddress https://{NODE_HOST} -``` - -### Set Wasp API Address - -Set the WASP API address. It is your configured `NODE_HOST` and the `/wasp/api` path. - -```sh -./wasp-cli wasp add my-node https://{NODE_HOST}/wasp/api -``` - -### Login - -Login to wasp using your credentials. You can update your current credentials or add new ones in the wasp dashboard. - -```sh -./wasp-cli login -``` -```sh title=Output -Username: wasp -Password: (default is wasp) -Successfully authenticated -``` - -### Obtain Peering Info - -Get your peering info which you will need to share with your peers: - -```sh -./wasp-cli peering info -``` -```sh title=Output -PubKey: 0x20a56daa0b5e86b196c37f802089a2b6007a655a12337d287f7313a214af2ec0 -PeeringURL: 0.0.0.0:4000 -``` - -Please note the PubKey: 0x20a56daa0b5e86b196c37f802089a2b6007a655a12337d287f7313a214af2ec0 output. -Send it together with your domain/IP to node operators that you want to peer with. - -### Wait for the other party to peer - -Wait until peer added you as trusted and access peer. - -### Use wasp-cli to add nodes as peers - -Now you can add your peer as trusted peer. - -```sh -./wasp-cli peering trust peer1 : -./wasp-cli peering trust peer2 : -``` - -### Add Chain - -Add the chain with its chain id and name: - - - - -```sh -./wasp-cli chain add iota-evm iota1pzt3mstq6khgc3tl0mwuzk3eqddkryqnpdxmk4nr25re2466uxwm28qqxu5 -``` - - - - -```sh -./wasp-cli chain add iota-evm-testnet tst1pzxsrr7apqkdzz633dyntmvxwtyvk029p39te5j0m33q6946h7akzv663zu -``` - - - - -```sh -./wasp-cli chain add shimmer-evm smr1prxvwqvwf7nru5q5xvh5thwg54zsm2y4wfnk6yk56hj3exxkg92mx20wl3s -``` - - - - -```sh -./wasp-cli chain add shimmer-evm-testnet rms1ppp00k5mmd2m8my8ukkp58nd3rskw6rx8l09aj35984k74uuc5u2cywn3ex -``` - - - - -### Activate - -Activate the chain using its name: - - - - - -```sh -./wasp-cli chain activate --chain iota-evm -``` - - - - -```sh -./wasp-cli chain activate --chain iota-evm-testnet -``` - - - - -```sh -./wasp-cli chain activate --chain shimmer-evm -``` - - - - -```sh -./wasp-cli chain activate --chain shimmer-evm-testnet -``` - - - - -### Add Peers as Access Nodes of the Chain - -Add the peers as access nodes. - -:::info - -This is normally only needed for peers that you plan to add as access nodes to your own node - -::: - -```sh -./wasp-cli chain access-nodes add --peers=peer1,peer2 -``` - -### Check if Wasp Synced - -You can follow the progress using `docker logs -f wasp`. If you chose to create a [full-archive node](#run-an-archive-node), this can take several minutes, maybe hours. - -### Test Your Endpoint - -You should have a working EVM JSON-RPC endpoint on: - - - - -``` -/wasp/api/v1/chains/iota1pzt3mstq6khgc3tl0mwuzk3eqddkryqnpdxmk4nr25re2466uxwm28qqxu5/evm -``` - - - - -``` -/wasp/api/v1/chains/tst1pzxsrr7apqkdzz633dyntmvxwtyvk029p39te5j0m33q6946h7akzv663zu/evm -``` - - - - - -``` -/wasp/api/v1/chains/smr1prxvwqvwf7nru5q5xvh5thwg54zsm2y4wfnk6yk56hj3exxkg92mx20wl3s/evm -``` - - - - -``` -/wasp/api/v1/chains/rms1ppp00k5mmd2m8my8ukkp58nd3rskw6rx8l09aj35984k74uuc5u2cywn3ex/evm -``` - - - diff --git a/docs/content/operator/iota-evm/how-tos/setting-up-a-chain.md b/docs/content/operator/iota-evm/how-tos/setting-up-a-chain.md deleted file mode 100644 index 0d0ab473798..00000000000 --- a/docs/content/operator/iota-evm/how-tos/setting-up-a-chain.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -description: 'Setting up a chain: requirements, configuration parameters, validators, and tests.' -image: /img/logo/WASP_logo_dark.png -tags: - - how-to - - isc - - setup ---- - -# Set Up a Chain - -:::note - -It is possible to run a "committee" of a single Wasp node, which is okay for testing purposes. - -However, in normal operation, multiple Wasp _nodes_ should be used. - -::: - -## Requirements - -- [`wasp-cli` configured](wasp-cli.md) to interact with your wasp node. - -## Trust Setup - -After starting all the `wasp` nodes, you should make them trust each other. Node operators should do this manually. It's -their responsibility to accept trusted nodes only. - -The operator can read their node's public key and PeeringURL by running `wasp-cli peering info`: - -```shell -wasp-cli peering info -``` - -Example response: - -```log -PubKey: 8oQ9xHWvfnShRxB22avvjbMyAumZ7EXKujuthqrzapNM -PeeringURL: 127.0.0.1:4000 -``` - -You should provide your `PubKey` and `PeeringURL` to other node operators. -They can use this info to trust and accept communications with your node. -That's done by invoking `wasp-cli peering trust `, e.g.: - -```shell -wasp-cli peering trust another-node 8oQ9xHWvfnShRxB22avvjbMyAumZ7EXKujuthqrzapNM 127.0.0.1:4000 -``` - -You can view the list of your wasp node's trusted peers by calling: - -```shell -wasp-cli peering list-trusted -``` - -All the nodes in a committee must trust each other to run the chain. - -## Start the Chain - -### Request Test Funds (only for Testnet) - -You can request test funds to safely develop your application by calling: - -```shell -wasp-cli request-funds -``` - -### Deploy the IOTA Smart Contracts Chain - -You can deploy your IOTA Smart Contracts chain by running: - -```shell -wasp-cli chain deploy --peers=foo,bar,baz --chain=mychain --block-keep-amount=10000 -``` - -The names in `--peers=foo,bar,baz` correspond to the names of the node's trusted peers. - -The `--chain=mychain` flag sets up an alias for the chain. -From now on, all chain commands will target this chain. - -The `--quorum` flag indicates the minimum number of nodes required to form a _consensus_. -The recommended formula to obtain this number is `floor(N*2/3)+1` where `N` is the number of nodes in your committee. - -The `--block-keep-amount` parameter determines how many blocks are stored in the [`blocklog`](/isc/../../references/iota-evm/core-contracts/blocklog) core contract. - -After deployment, the chain must be activated by the node operators of all peers. - -```shell -wasp-cli chain add # adds the chain to the wasp-cli config, can be skipped on the wasp-cli that initiated the deployment -wasp-cli chain activate --chain= -``` - -## Test If It Works - -You can check that the chain was deployed correctly in the Wasp node dashboard (`/wasp/dashboard` when using `node-docker-setup`). -Note that the chain was deployed with some [core contracts](/isc/../../references/iota-evm/core-contracts/overview). - -You should also have an EVM-JSONRPC server opened on: - -```info -/chain//evm -``` - -### Deploying a Wasm Contract - -:::warning -The WASM _VM_ is experimental. However, similar commands can be used to interact with the core contracts -::: - -Now you can deploy a Wasm contract to the chain: - -```shell -wasp-cli chain deploy-contract wasmtime inccounter "inccounter SC" tools/cluster/tests/wasm/inccounter_bg.wasm -``` - -The `inccounter_bg.wasm` file is a precompiled Wasm contract included in the Wasp repo as an example. - -If you recheck the dashboard, you should see that the `inccounter` contract is listed in the chain. - -### Interacting With a Smart Contract - -You can interact with a contract by calling its exposed functions and views. - -For instance, the [`inccounter`](https://github.com/iotaledger/wasp/tree/master/contracts/wasm/inccounter/src) contract -exposes the `increment` function, which simply increments a counter stored in the state. It also has the `getCounter` -view that returns the current value of the counter. - -You can call the `getCounter` view by running: - -```shell -wasp-cli chain call-view inccounter getCounter | wasp-cli decode string counter int -``` - -Example response: - -```log -counter: 0 -``` - -:::note - -The part after `|` is necessary because the return value is encoded, and you need to know the _schema_ to -decode it. **The schema definition is in its early stages and will likely change in the future.** - -::: - -You can now call the `increment` function by running: - -```shell -wasp-cli chain post-request inccounter increment -``` - -After the committee has processed the request, you should get a new -counter value after calling `getCounter`: - -```shell -wasp-cli chain call-view inccounter getCounter | wasp-cli decode string counter int -``` - -Example response: - -```log -counter: 1 -``` - -### Troubleshooting - -Common issues can be caused by using an incompatible version of `wasp` / `wasp-cli`. -You can verify that `wasp-cli` and `wasp` nodes are on the same version by running: - -```shell -wasp-cli check-versions -``` diff --git a/docs/content/operator/iota-evm/how-tos/wasp-cli.md b/docs/content/operator/iota-evm/how-tos/wasp-cli.md deleted file mode 100644 index 3edd7866f65..00000000000 --- a/docs/content/operator/iota-evm/how-tos/wasp-cli.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: How to configure wasp-cli. Requirements and configuration parameters. -image: /img/logo/WASP_logo_dark.png -tags: - - cli - - how-to - - isc ---- - -# Configure wasp-cli - -You can use these step-by-step instructions on how to use wasp-cli to interact with Wasp nodes on the Hornet network. - -## Download wasp-cli - -Download the latest wasp-cli binary from the repo [releases page](https://github.com/iotaledger/wasp/releases). -(For ease of use, it is recommended to add `wasp-cli` to your system `PATH`). - -## Configuration - -You can create a basic default configuration by running: - -```shell -wasp-cli init -``` - -This command will create a configuration file named `wasp-cli.json` in `~/.wasp-cli/wasp-cli.json` (you can change the default with the `-c, --config` flag). - -:::info Wallet Provider - -By default wasp-cli will store the seed in your OS keychain. You can change this behavior by running - -```shell -wasp-cli wallet-provider (keychain, sdk_ledger, sdk_stronghold) [flags] -``` - -::: - -:::info Deprecated Seed storage - -If you use a version older then [1.0.3](https://github.com/iotaledger/wasp/releases/tag/v1.0.3-alpha.1) your seed is still stored as plain text. You can migrate to the keychain by using the following command: - -```shell -wasp-cli wallet-migrate (keychain) [flags] -``` - -::: - -After this, you will need to tell the `wasp-cli` the location of the _Hornet node_ and the committee of Wasp nodes: - -```shell -wasp-cli set l1.apiaddress http://localhost:14265 -# the faucet only exists for test networks -wasp-cli set l1.faucetaddress http://localhost:8091 - -# You can add as many nodes as you'd like -wasp-cli wasp add wasp-0 127.0.0.1:9090 -wasp-cli wasp add wasp-1 127.0.0.1:9091 -``` - -If you configure the Wasp node to use JWT authentication, you will need to log in -after you save the configuration. - -```shell -wasp-cli login -``` diff --git a/docs/content/operator/iota-evm/reference/configuration.md b/docs/content/operator/iota-evm/reference/configuration.md deleted file mode 100755 index a8dbc31e0d7..00000000000 --- a/docs/content/operator/iota-evm/reference/configuration.md +++ /dev/null @@ -1,563 +0,0 @@ ---- -# !!! DO NOT MODIFY !!! -# This file is auto-generated by the gendoc tool based on the source code of the app. -description: This section describes the configuration parameters and their types for WASP. -tags: - - reference ---- - -# Core Configuration - -WASP uses a JSON standard format as a config file. If you are unsure about JSON syntax, you can find more information in the [official JSON specs](https://www.json.org). - -You can change the path of the config file by using the `-c` or `--config` argument while executing `wasp` executable. - -For example: - -```shell -wasp -c config_defaults.json -``` - -You can always get the most up-to-date description of the config parameters by running: - -```shell -wasp -h --full -``` - -##
1. Application - -| Name | Description | Type | Default value | -| ------------------------- | ------------------------------------------------------ | ------- | ------------- | -| checkForUpdates | Whether to check for updates of the application or not | boolean | true | -| [shutdown](#app_shutdown) | Configuration for shutdown | object | | - -### Shutdown - -| Name | Description | Type | Default value | -| ------------------------ | ------------------------------------------------------------------------------------------------------ | ------ | ------------- | -| stopGracePeriod | The maximum time to wait for background processes to finish during shutdown before terminating the app | string | "5m" | -| [log](#app_shutdown_log) | Configuration for Shutdown Log | object | | - -### Shutdown Log - -| Name | Description | Type | Default value | -| -------- | --------------------------------------------------- | ------- | -------------- | -| enabled | Whether to store self-shutdown events to a log file | boolean | true | -| filePath | The file path to the self-shutdown log | string | "shutdown.log" | - -Example: - -```json -{ - "app": { - "checkForUpdates": true, - "shutdown": { - "stopGracePeriod": "5m", - "log": { - "enabled": true, - "filePath": "shutdown.log" - } - } - } -} -``` - -## 2. Logger - -| Name | Description | Type | Default value | -| ---------------------------------------- | --------------------------------------------------------------------------- | ------- | ------------- | -| level | The minimum enabled logging level | string | "info" | -| disableCaller | Stops annotating logs with the calling function's file name and line number | boolean | true | -| disableStacktrace | Disables automatic stacktrace capturing | boolean | false | -| stacktraceLevel | The level stacktraces are captured and above | string | "panic" | -| encoding | The logger's encoding (options: "json", "console") | string | "console" | -| [encodingConfig](#logger_encodingconfig) | Configuration for encodingConfig | object | | -| outputPaths | A list of URLs, file paths or stdout/stderr to write logging output to | array | stdout | -| disableEvents | Prevents log messages from being raced as events | boolean | true | - -### EncodingConfig - -| Name | Description | Type | Default value | -| ----------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------------- | -| timeEncoder | Sets the logger's timestamp encoding. (options: "nanos", "millis", "iso8601", "rfc3339" and "rfc3339nano") | string | "rfc3339" | - -Example: - -```json -{ - "logger": { - "level": "info", - "disableCaller": true, - "disableStacktrace": false, - "stacktraceLevel": "panic", - "encoding": "console", - "encodingConfig": { - "timeEncoder": "rfc3339" - }, - "outputPaths": [ - "stdout" - ], - "disableEvents": true - } -} -``` - -## 3. INX - -| Name | Description | Type | Default value | -| --------------------- | -------------------------------------------------------------------------------------------------- | ------ | ---------------- | -| address | The INX address to which to connect to | string | "localhost:9029" | -| maxConnectionAttempts | The amount of times the connection to INX will be attempted before it fails (1 attempt per second) | uint | 30 | -| targetNetworkName | The network name on which the node should operate on (optional) | string | "" | - -Example: - -```json -{ - "inx": { - "address": "localhost:9029", - "maxConnectionAttempts": 30, - "targetNetworkName": "" - } -} -``` - -## 4. Cache - -| Name | Description | Type | Default value | -| ------------------ | -------------------------------------- | ------- | ------------- | -| cacheSize | Cache size | string | "64MiB" | -| cacheStatsInterval | Interval for printing cache statistics | string | "30s" | -| enabled | Whether the cache plugin is enabled | boolean | true | - -Example: - -```json -{ - "cache": { - "cacheSize": "64MiB", - "cacheStatsInterval": "30s", - "enabled": true - } -} -``` - -## 5. Database - -| Name | Description | Type | Default value | -| ---------------------------- | ---------------------------------------- | ------- | ------------- | -| engine | The used database engine (rocksdb/mapdb) | string | "rocksdb" | -| [chainState](#db_chainstate) | Configuration for chainState | object | | -| debugSkipHealthCheck | Ignore the check for corrupted databases | boolean | true | - -### ChainState - -| Name | Description | Type | Default value | -| --------- | -------------------------------------------- | ------ | -------------------- | -| path | The path to the chain state databases folder | string | "waspdb/chains/data" | -| cacheSize | Size of the RocksDB block cache | uint | 33554432 | - -Example: - -```json -{ - "db": { - "engine": "rocksdb", - "chainState": { - "path": "waspdb/chains/data", - "cacheSize": 33554432 - }, - "debugSkipHealthCheck": true - } -} -``` - -## 6. P2p - -| Name | Description | Type | Default value | -| ------------------------- | -------------------------- | ------ | ------------- | -| [identity](#p2p_identity) | Configuration for identity | object | | -| [db](#p2p_db) | Configuration for Database | object | | - -### Identity - -| Name | Description | Type | Default value | -| ---------- | ------------------------------------------------------- | ------ | ------------------------------ | -| privateKey | Private key used to derive the node identity (optional) | string | "" | -| filePath | The path to the node identity PEM file | string | "waspdb/identity/identity.key" | - -### Database - -| Name | Description | Type | Default value | -| ---- | ---------------------------- | ------ | ----------------- | -| path | The path to the p2p database | string | "waspdb/p2pstore" | - -Example: - -```json -{ - "p2p": { - "identity": { - "privateKey": "", - "filePath": "waspdb/identity/identity.key" - }, - "db": { - "path": "waspdb/p2pstore" - } - } -} -``` - -## 7. Registries - -| Name | Description | Type | Default value | -| -------------------------------------------- | -------------------------------- | ------ | ------------- | -| [chains](#registries_chains) | Configuration for chains | object | | -| [dkShares](#registries_dkshares) | Configuration for dkShares | object | | -| [trustedPeers](#registries_trustedpeers) | Configuration for trustedPeers | object | | -| [consensusState](#registries_consensusstate) | Configuration for consensusState | object | | - -### Chains - -| Name | Description | Type | Default value | -| -------- | ----------------------------------- | ------ | ----------------------------------- | -| filePath | The path to the chain registry file | string | "waspdb/chains/chain_registry.json" | - -### DkShares - -| Name | Description | Type | Default value | -| ---- | -------------------------------------------------------- | ------ | ----------------- | -| path | The path to the distributed key shares registries folder | string | "waspdb/dkshares" | - -### TrustedPeers - -| Name | Description | Type | Default value | -| -------- | ------------------------------------------- | ------ | --------------------------- | -| filePath | The path to the trusted peers registry file | string | "waspdb/trusted_peers.json" | - -### ConsensusState - -| Name | Description | Type | Default value | -| ---- | ------------------------------------------------- | ------ | ------------------------- | -| path | The path to the consensus state registries folder | string | "waspdb/chains/consensus" | - -Example: - -```json -{ - "registries": { - "chains": { - "filePath": "waspdb/chains/chain_registry.json" - }, - "dkShares": { - "path": "waspdb/dkshares" - }, - "trustedPeers": { - "filePath": "waspdb/trusted_peers.json" - }, - "consensusState": { - "path": "waspdb/chains/consensus" - } - } -} -``` - -## 8. Peering - -| Name | Description | Type | Default value | -| ---------- | ---------------------------------------------------- | ------ | -------------- | -| peeringURL | Node host address as it is recognized by other peers | string | "0.0.0.0:4000" | -| port | Port for Wasp committee connection/peering | int | 4000 | - -Example: - -```json -{ - "peering": { - "peeringURL": "0.0.0.0:4000", - "port": 4000 - } -} -``` - -## 9. Chains - -| Name | Description | Type | Default value | -| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------- | -| broadcastUpToNPeers | Number of peers an offledger request is broadcasted to | int | 2 | -| broadcastInterval | Time between re-broadcast of offledger requests; 0 value means that re-broadcasting is disabled | string | "0s" | -| apiCacheTTL | Time to keep processed offledger requests in api cache | string | "5m" | -| pullMissingRequestsFromCommittee | Whether or not to pull missing requests from other committee members | boolean | true | -| deriveAliasOutputByQuorum | False means we propose own AliasOutput, true - by majority vote. | boolean | true | -| pipeliningLimit | -1 -- infinite, 0 -- disabled, X -- build the chain if there is up to X transactions unconfirmed by L1. | int | -1 | -| postponeRecoveryMilestones | Number of milestones to wait until a chain transition is considered as rejected | int | 3 | -| consensusDelay | Minimal delay between consensus runs. | string | "500ms" | -| recoveryTimeout | Time after which another consensus attempt is made. | string | "20s" | -| redeliveryPeriod | The resend period for msg. | string | "2s" | -| printStatusPeriod | The period to print consensus instance status. | string | "3s" | -| consensusInstsInAdvance | | int | 3 | -| awaitReceiptCleanupEvery | For every this number AwaitReceipt will be cleaned up | int | 100 | -| mempoolTTL | Time that requests are allowed to sit in the mempool without being processed | string | "24h" | -| mempoolMaxOffledgerInPool | Maximum number of off-ledger requests kept in the mempool | int | 2000 | -| mempoolMaxOnledgerInPool | Maximum number of on-ledger requests kept in the mempool | int | 1000 | -| mempoolMaxTimedInPool | Maximum number of timed on-ledger requests kept in the mempool | int | 100 | -| mempoolMaxOffledgerToPropose | Maximum number of off-ledger requests to propose for the next block | int | 500 | -| mempoolMaxOnledgerToPropose | Maximum number of on-ledger requests to propose for the next block (includes timed requests) | int | 100 | -| mempoolOnLedgerRefreshMinInterval | Minimum interval to try to refresh the list of on-ledger requests after some have been dropped from the pool (this interval is introduced to avoid dropping/refreshing cycle if there are too many requests on L1 to process) | string | "10m" | - -Example: - -```json -{ - "chains": { - "broadcastUpToNPeers": 2, - "broadcastInterval": "0s", - "apiCacheTTL": "5m", - "pullMissingRequestsFromCommittee": true, - "deriveAliasOutputByQuorum": true, - "pipeliningLimit": -1, - "postponeRecoveryMilestones": 3, - "consensusDelay": "500ms", - "recoveryTimeout": "20s", - "redeliveryPeriod": "2s", - "printStatusPeriod": "3s", - "consensusInstsInAdvance": 3, - "awaitReceiptCleanupEvery": 100, - "mempoolTTL": "24h", - "mempoolMaxOffledgerInPool": 2000, - "mempoolMaxOnledgerInPool": 1000, - "mempoolMaxTimedInPool": 100, - "mempoolMaxOffledgerToPropose": 500, - "mempoolMaxOnledgerToPropose": 100, - "mempoolOnLedgerRefreshMinInterval": "10m" - } -} -``` - -## 10. Snapshots - -| Name | Description | Type | Default value | -| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------- | -| snapshotsToLoad | List of snapshots to load; can be either single block hash of a snapshot (if a single chain has to be configured) or list of `:` to configure many chains | array | | -| period | How often state snapshots should be made: 1000 meaning "every 1000th state", 0 meaning "making snapshots is disabled" | uint | 0 | -| delay | How many states should pass before snapshot is produced | uint | 20 | -| localPath | The path to the snapshots folder in this node's disk | string | "waspdb/snap" | -| networkPaths | The list of paths to the remote (http(s)) snapshot locations; each of listed locations must contain 'INDEX' file with list of snapshot files | array | | - -Example: - -```json -{ - "snapshots": { - "snapshotsToLoad": [], - "period": 0, - "delay": 20, - "localPath": "waspdb/snap", - "networkPaths": [] - } -} -``` - -## 11. StateManager - -| Name | Description | Type | Default value | -| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | ------------- | -| blockCacheMaxSize | How many blocks may be stored in cache before old ones start being deleted | int | 1000 | -| blockCacheBlocksInCacheDuration | How long should the block stay in block cache before being deleted | string | "1h" | -| blockCacheBlockCleaningPeriod | How often should the block cache be cleaned | string | "1m" | -| stateManagerGetBlockNodeCount | How many nodes should get block request be sent to | int | 5 | -| stateManagerGetBlockRetry | How often get block requests should be repeated | string | "3s" | -| stateManagerRequestCleaningPeriod | How often requests waiting for response should be checked for expired context | string | "5m" | -| stateManagerStatusLogPeriod | How often state manager status information should be written to log | string | "1m" | -| stateManagerTimerTickPeriod | How often timer tick fires in state manager | string | "1s" | -| pruningMinStatesToKeep | This number of states will always be available in the store; if 0 - store pruning is disabled | int | 10000 | -| pruningMaxStatesToDelete | On single store pruning attempt at most this number of states will be deleted; NOTE: pruning takes considerable amount of time; setting this parameter large may seriously damage Wasp responsiveness if many blocks require pruning | int | 10 | - -Example: - -```json -{ - "stateManager": { - "blockCacheMaxSize": 1000, - "blockCacheBlocksInCacheDuration": "1h", - "blockCacheBlockCleaningPeriod": "1m", - "stateManagerGetBlockNodeCount": 5, - "stateManagerGetBlockRetry": "3s", - "stateManagerRequestCleaningPeriod": "5m", - "stateManagerStatusLogPeriod": "1m", - "stateManagerTimerTickPeriod": "1s", - "pruningMinStatesToKeep": 10000, - "pruningMaxStatesToDelete": 10 - } -} -``` - -## 12. Validator - -| Name | Description | Type | Default value | -| ------- | ------------------------------------------------------------------------------------------------------------------ | ------ | ------------- | -| address | Bech32 encoded address to identify the node (as access node on gov contract and to collect validator fee payments) | string | "" | - -Example: - -```json -{ - "validator": { - "address": "" - } -} -``` - -## 13. Write-Ahead Logging - -| Name | Description | Type | Default value | -| ----------- | ---------------------------------------------------------------- | ------- | ------------- | -| loadToStore | Load blocks from "write-ahead log" to the store on node start-up | boolean | false | -| enabled | Whether the "write-ahead logging" is enabled | boolean | true | -| path | The path to the "write-ahead logging" folder | string | "waspdb/wal" | - -Example: - -```json -{ - "wal": { - "loadToStore": false, - "enabled": true, - "path": "waspdb/wal" - } -} -``` - -## 14. Web API - -| Name | Description | Type | Default value | -| ------------------------- | ------------------------------------------------------------------------------------------ | ------- | --------------------- | -| enabled | Whether the web api plugin is enabled | boolean | true | -| bindAddress | The bind address for the node web api | string | "0.0.0.0:9090" | -| [auth](#webapi_auth) | Configuration for auth | object | | -| indexDbPath | Directory for storing indexes of historical data (only archive nodes will create/use them) | string | "waspdb/chains/index" | -| [limits](#webapi_limits) | Configuration for limits | object | | -| debugRequestLoggerEnabled | Whether the debug logging for requests should be enabled | boolean | false | - -### Auth - -| Name | Description | Type | Default value | -| ----------------------- | -------------------------------------- | ------ | ------------- | -| scheme | Selects which authentication to choose | string | "jwt" | -| [jwt](#webapi_auth_jwt) | Configuration for JWT Auth | object | | - -### JWT Auth - -| Name | Description | Type | Default value | -| -------- | ------------------ | ------ | ------------- | -| duration | Jwt token lifetime | string | "24h" | - -### Limits - -| Name | Description | Type | Default value | -| --------------------------------- | ----------------------------------------------------------------------------- | ------ | ------------- | -| timeout | The timeout after which a long running operation will be canceled | string | "30s" | -| readTimeout | The read timeout for the HTTP request body | string | "10s" | -| writeTimeout | The write timeout for the HTTP response body | string | "1m" | -| maxBodyLength | The maximum number of characters that the body of an API call may contain | string | "2M" | -| maxTopicSubscriptionsPerClient | Defines the max amount of subscriptions per client. 0 = deactivated (default) | int | 0 | -| confirmedStateLagThreshold | The threshold that define a chain is unsynchronized | uint | 2 | -| [jsonrpc](#webapi_limits_jsonrpc) | Configuration for jsonrpc | object | | - -### Jsonrpc - -| Name | Description | Type | Default value | -| ----------------------------------- | -------------------------------------------------------------- | ------ | ------------- | -| maxBlocksInLogsFilterRange | Maximum amount of blocks in eth_getLogs filter range | int | 1000 | -| maxLogsInResult | Maximum amount of logs in eth_getLogs result | int | 10000 | -| websocketRateLimitMessagesPerSecond | The websocket rate limit (messages per second) | int | 20 | -| websocketRateLimitBurst | The websocket burst limit | int | 5 | -| websocketConnectionCleanupDuration | Defines in which interval stale connections will be cleaned up | string | "5m" | -| websocketClientBlockDuration | The duration a misbehaving client will be blocked | string | "5m" | - -Example: - -```json -{ - "webapi": { - "enabled": true, - "bindAddress": "0.0.0.0:9090", - "auth": { - "scheme": "jwt", - "jwt": { - "duration": "24h" - } - }, - "indexDbPath": "waspdb/chains/index", - "limits": { - "timeout": "30s", - "readTimeout": "10s", - "writeTimeout": "1m", - "maxBodyLength": "2M", - "maxTopicSubscriptionsPerClient": 0, - "confirmedStateLagThreshold": 2, - "jsonrpc": { - "maxBlocksInLogsFilterRange": 1000, - "maxLogsInResult": 10000, - "websocketRateLimitMessagesPerSecond": 20, - "websocketRateLimitBurst": 5, - "websocketConnectionCleanupDuration": "5m", - "websocketClientBlockDuration": "5m" - } - }, - "debugRequestLoggerEnabled": false - } -} -``` - -## 15. Profiling - -| Name | Description | Type | Default value | -| ----------- | ------------------------------------------------- | ------- | ---------------- | -| enabled | Whether the profiling component is enabled | boolean | false | -| bindAddress | The bind address on which the profiler listens on | string | "localhost:6060" | - -Example: - -```json -{ - "profiling": { - "enabled": false, - "bindAddress": "localhost:6060" - } -} -``` - -## 16. ProfilingRecorder - -| Name | Description | Type | Default value | -| ------- | ----------------------------------------------- | ------- | ------------- | -| enabled | Whether the ProfilingRecorder plugin is enabled | boolean | false | - -Example: - -```json -{ - "profilingRecorder": { - "enabled": false - } -} -``` - -## 17. Prometheus - -| Name | Description | Type | Default value | -| ----------- | ------------------------------------------------------------ | ------- | -------------- | -| enabled | Whether the prometheus plugin is enabled | boolean | true | -| bindAddress | The bind address on which the Prometheus exporter listens on | string | "0.0.0.0:2112" | - -Example: - -```json -{ - "prometheus": { - "enabled": true, - "bindAddress": "0.0.0.0:2112" - } -} -``` diff --git a/docs/content/operator/iota-evm/reference/metrics.md b/docs/content/operator/iota-evm/reference/metrics.md deleted file mode 100644 index b98cee134ff..00000000000 --- a/docs/content/operator/iota-evm/reference/metrics.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: IOTA Smart Contract Protocol is IOTA's solution for running smart contracts on top of the IOTA _tangle_. -image: /img/logo/WASP_logo_dark.png -tags: - - reference ---- - -# Exposed Metrics - -Refer to the testnet endpoints description for access details. - -| Metric | Description | -| ------------------------------------------ | ---------------------------------------------------- | -| `wasp_off_ledger_requests_counter` | Off-ledger requests per chain. | -| `wasp_on_ledger_request_counter` | On-ledger requests per chain. | -| `wasp_processed_request_counter` | Total number of requests processed. | -| `messages_received_per_chain` | Number of messages received per chain. | -| `receive_requests_acknowledgement_message` | Number of request acknowledgment messages per chain. | -| `request_processing_time` | Time to process request. | -| `vm_run_time` | Time it takes to run the VM. | diff --git a/docs/content/operator/iota-full-node.mdx b/docs/content/operator/iota-full-node.mdx index 2c721da81f9..1fad455a326 100644 --- a/docs/content/operator/iota-full-node.mdx +++ b/docs/content/operator/iota-full-node.mdx @@ -112,22 +112,22 @@ Open a terminal or console to the `iota` directory you downloaded in the previou 1. Make a copy of the [Full node YAML template](https://github.com/iotaledger/iota/blob/develop/crates/iota-config/data/fullnode-template.yaml): `cp crates/iota-config/data/fullnode-template.yaml fullnode.yaml` 1. Download the genesis blob for the network to use: - - [Alphanet genesis blob](https://dbfiles.iota-rebased-alphanet.iota.cafe/genesis.blob): - `curl -fLJO https://dbfiles.iota-rebased-alphanet.iota.cafe/genesis.blob` + - [Testnet genesis blob](https://dbfiles.iota-rebased-testnet.iota.cafe/genesis.blob): + `curl -fLJO https://dbfiles.iota-rebased-testnet.iota.cafe/genesis.blob` - [Testnet genesis blob](https://github.com/iotaledger/iota/TODO): `curl -fLJO TODO` - [Mainnet genesis blob](https://github.com/iotaledger/iota/TODO): `curl -fLJO TODO` -1. For Alphanet or Testnet: Edit the `fullnode.yaml` file to include peer nodes for state synchronization. Append the following to the end of the current configuration: +1. For Devnet or Testnet: Edit the `fullnode.yaml` file to include peer nodes for state synchronization. Append the following to the end of the current configuration: - + ```yaml p2p-config: seed-peers: - - address: /dns/access-0.r.iota-rebased-alphanet.iota.cafe/udp/8084 + - address: /dns/access-0.r.iota-rebased-devnet.iota.cafe/udp/8084 peer-id: 10cbea76ea5ec3f7ee827f0c11f612f0059949127876dd964b09304bf8808d18 ``` @@ -137,7 +137,7 @@ Open a terminal or console to the `iota` directory you downloaded in the previou ```yaml p2p-config: seed-peers: - - address: TODO # Example: /dns/yto-tnt-ssfn-01.testnet.iota.io/udp/8084 + - address: TODO # Example: /dns/yto-tnt-ssfn-01.testnet.iota.cafe/udp/8084 peer-id: TODO # Example: 2ed53564d5581ded9b6773970ac2f1c84d39f9edf01308ff5a1ffe09b1add7b3 ``` diff --git a/docs/content/references/cli/client.mdx b/docs/content/references/cli/client.mdx index f5728905ff7..87060609c08 100644 --- a/docs/content/references/cli/client.mdx +++ b/docs/content/references/cli/client.mdx @@ -3,7 +3,6 @@ title: IOTA Client CLI description: The IOTA Client CLI provides command-level access to interact with the IOTA network. --- -import AlphaNet from "../../_snippets/alphanet.mdx"; import CliCheckInstall from "../../_snippets/cli-check-install.mdx"; The IOTA CLI `client` command provides command-level access to interact with the IOTA network. Typical uses for `iota client` include publishing Move smart contracts, getting the information of an object, executing transactions, or managing addresses. @@ -80,20 +79,18 @@ Use the `iota client envs` command to find the network environments set up in th ╭────────┬─────────────────────────────────────┬────────╮ │ alias │ url │ active │ ├────────┼─────────────────────────────────────┼────────┤ -│ devnet │ https://fullnode.devnet.iota.io:443 │ * │ +│ devnet │ https://api.devnet.iota.cafe:443 │ * │ ╰────────┴─────────────────────────────────────┴────────╯ ``` - - ### Create network environment Use `client new-env` to add details for a new network environment. This example creates an environment pointer to IOTA Mainnet. Setting the `alias` value makes referencing the environment less prone to typographical errors. After running this command, IOTA updates your client.yaml file in `~/.iota/iota_config` with the new information. ``` -$ iota client new-env --alias=mainnet --rpc https://fullnode.mainnet.iota.io:443 +$ iota client new-env --alias=testnet --rpc https://api.testnet.iota.cafe:443 -Added new IOTA env [mainnet] to config. +Added new IOTA env [testnet] to config. ``` ### Set current environment @@ -101,9 +98,9 @@ Added new IOTA env [mainnet] to config. Use the `iota client switch` command to change the current network. This example switches the current network to `mainnet`. ```shell -$ iota client switch --env mainnet +$ iota client switch --env testnet -Active environment switched to [mainnet] +Active environment switched to [testnet] ``` If you run `iota client envs` after this command, you see the asterisk in the `active` column on the `mainnet` row of the table. @@ -113,16 +110,16 @@ If you run `iota client envs` after this command, you see the asterisk in the `a Use the `iota client active-address` command to reveal the current address. The CLI uses the current active address to execute address-specific CLI commands (like `iota client objects`) when you don't provide them with a IOTA address value. ```shell -$ iota client active-address +iota client active-address 0x514692f08249c3e9951234ce29074695840422564bff85e424b56de462913e0d ``` ### Request a IOTA coin from faucet -If you use one of the standard public RPCs (for example, `fullnode.testnet.iota.io:443`), you can use the `faucet` command to request gas coins. If you use a custom faucet service, then pass in the URL to the faucet using the `--url` option. +If you use one of the standard public RPCs (for example, `api.testnet.iota.cafe:443`), you can use the `faucet` command to request gas coins. If you use a custom faucet service, then pass in the URL to the faucet using the `--url` option. ```shell -$ iota client faucet +iota client faucet Request successful. It can take up to 1 minute to get the coin. Run iota client gas to check your gas coins. ``` @@ -209,7 +206,7 @@ Both `pay` and `transfer` have a few sister commands: `pay-iota`, `pay-all-iota` The differences between these commands are: - commands that end in `-iota` deal with Iota's native coin, and they use the input coints to pay for gas and for transferring IOTA or the object. -- `pay-` commands typically deal with coins and handle gas smashing for you, whereas `transfer` commands can handle the transfer of any object that has public transfer, meaning any object that has the `store` ability. +- `pay-` commands typically deal with coins and handle (coin merging)[../../developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx] for you, whereas `transfer` commands can handle the transfer of any object that has public transfer, meaning any object that has the `store` ability. - `pay` commands allow you to send coins to multiple recipients, whereas `transfer` commands only accept one recipient. - `pay-all-iota` is a special case of `pay-iota` that offers a way to transfer the entire balance after smashing. - `transfer-iota` is a legacy command and has been entirely superseded by `pay-iota` or `pay-all-iota` depending on whether an amount is specified or not. diff --git a/docs/content/references/cli/console.mdx b/docs/content/references/cli/console.mdx index 52b9ef9f7c6..af0f5272b7c 100644 --- a/docs/content/references/cli/console.mdx +++ b/docs/content/references/cli/console.mdx @@ -27,7 +27,7 @@ Active address: 0x3...235 Keystore Type: File Keystore Path: Some("/Users/user/.iota/iota_config/iota.keystore") Active environment: testnet -RPC URL: https://fullnode.testnet.iota.io:443 +RPC URL: https://api.testnet.iota.cafe:443 Connecting to IOTA full node. API version 1.13.0 Available RPC methods: ["iota_devInspectTransactionBlock", "iota_dryRunTransactionBlock", "iota_executeTransactionBlock", diff --git a/docs/content/references/cli/ptb.mdx b/docs/content/references/cli/ptb.mdx index c85de0d95fa..7f50f2d925c 100644 --- a/docs/content/references/cli/ptb.mdx +++ b/docs/content/references/cli/ptb.mdx @@ -51,7 +51,7 @@ Options: The main philosophy behind the CLI PTB support is to enable a user to build and execute a PTB from the command line. Bash scripts can be used to construct and execute the PTB just as you would do from the command line, providing great flexibility when it comes to automating different tasks. -Besides using existing [traditional PTB](../../developer/iota-101/transactions/ptb/prog-txn-blocks.mdx) related concepts, we introduce a few new and important concepts for this command. +Besides using existing [traditional PTB](../../developer/iota-101/transactions/ptb/programmable-transaction-blocks) related concepts, we introduce a few new and important concepts for this command. :::warning diff --git a/docs/content/references/cli/validator.mdx b/docs/content/references/cli/validator.mdx index 9c80b24227e..7668dd53c0c 100644 --- a/docs/content/references/cli/validator.mdx +++ b/docs/content/references/cli/validator.mdx @@ -169,13 +169,13 @@ $ iota validator display-metadata 0x8359fff1dc629bad933cf1781564a6d98a7277ac391e "networkPubkeyBytes": "1JiYHZHJFtuxim7woWhPGdBIe4QPoyvLVgpCRRZwUWg=", "protocolPubkeyBytes": "XhjH8q+isy4LP4GS1txLJYl+KQ9kV7b5dnu6C2x4XIk=", "proofOfPossessionBytes": "ovRMD5206HPBOoTfx+TgB5NXIKpbra4AuJRVGFSaaFbP2hAAZBDWpP1OD0Nyackf", - "name": "validator-2.r.iota-rebased-alphanet", - "description": "validator-2.r.iota-rebased-alphanet", + "name": "validator-2.r.iota-rebased-testnet", + "description": "validator-2.r.iota-rebased-testnet", "imageUrl": "", "projectUrl": "", - "netAddress": "/dns/validator-2.r.iota-rebased-alphanet.iota.cafe/tcp/8080/http", - "p2pAddress": "/dns/validator-2.r.iota-rebased-alphanet.iota.cafe/udp/8084", - "primaryAddress": "/dns/validator-2.r.iota-rebased-alphanet.iota.cafe/udp/8081", + "netAddress": "/dns/validator-2.r.iota-rebased-testnet.iota.cafe/tcp/8080/http", + "p2pAddress": "/dns/validator-2.r.iota-rebased-testnet.iota.cafe/udp/8084", + "primaryAddress": "/dns/validator-2.r.iota-rebased-testnet.iota.cafe/udp/8081", "nextEpochAuthorityPubkeyBytes": null, "nextEpochProofOfPossession": null, "nextEpochNetworkPubkeyBytes": null, diff --git a/docs/content/references/contribute/style-guide.mdx b/docs/content/references/contribute/style-guide.mdx index 29f27d49712..04b796051fe 100644 --- a/docs/content/references/contribute/style-guide.mdx +++ b/docs/content/references/contribute/style-guide.mdx @@ -556,7 +556,7 @@ Measurements should be written as numerals. ## Links -Always use relative links when linking to topics on docs.iota.io. +Always use relative links when linking to topics on docspnpm .iota.org. Include the `.mdx` extension when creating internal links so that they also work from the source file in GitHub. diff --git a/docs/content/references/event-query-and-subscription.mdx b/docs/content/references/event-query-and-subscription.mdx index 8bc190554b2..3390e6617d3 100644 --- a/docs/content/references/event-query-and-subscription.mdx +++ b/docs/content/references/event-query-and-subscription.mdx @@ -19,7 +19,7 @@ A IOTA node emits the following types of events: ## Move event -Move calls emit Move events. You can [define custom events](https://examples.iota.io/basics/events.html) in Move contracts. +Move calls emit Move events. You can define custom events in Move contracts. ### Attributes diff --git a/docs/content/references/exchange-integration-guide.mdx b/docs/content/references/exchange-integration-guide.mdx index 8e49be0a241..0cd4d7663f6 100644 --- a/docs/content/references/exchange-integration-guide.mdx +++ b/docs/content/references/exchange-integration-guide.mdx @@ -3,7 +3,6 @@ title: Exchange Integration Guide sidebar_label: Exchange Integration slug: /exchange-integration-guide --- -import AlphaNet from "../_snippets/alphanet.mdx"; This topic describes how to integrate IOTA, the token native to the IOTA network, into a cryptocurrency exchange. The specific requirements and processes to implement an integration vary between exchanges. Rather than provide a step-by-step guide, this topic provides information about the primary tasks necessary to complete an integration. After the guidance about how to configure an integration, you can also find information and code samples related to staking on the IOTA network. @@ -114,10 +113,8 @@ You can track balance changes by calling `iota_getBalance` at predefined interva The following bash example demonstrates how to use `iota_getBalance` for address 0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3. If you use a network other than Devnet, replace the value for `rpc` with the URL to the appropriate Full node. - - ```bash -rpc="https://fullnode.devnet.iota.io:443" +rpc="https://api.devnet.iota.cafe:443" address="0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3" data="{\"jsonrpc\": \"2.0\", \"method\": \"iota_getBalance\", \"id\": 1, \"params\": [\"$address\"]}" curl -X POST -H 'Content-type: application/json' --data-raw "$data" $rpc @@ -148,7 +145,7 @@ use iota_sdk::{IOTAClient, IOTAClientBuilder}; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let iota = IOTAClientBuilder::default().build( - "https://fullnode.devnet.iota.io:443", + "https://api.devnet.iota.cafe:443", ).await.unwrap(); let address = IOTAAddress::from_str("0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3")?; let objects = iota.read_api().get_balance(address).await?; @@ -163,7 +160,7 @@ You can also track the balance for an address by subscribing to all of the event The following example demonstrates how to filter events for an address using bash and cURL: ```bash -rpc="https://fullnode.devnet.iota.io:443" +rpc="https://api.devnet.iota.cafe:443" address="0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3" data="{\"jsonrpc\": \"2.0\", \"id\":1, \"method\": \"iota_getEvents\", \"params\": [{\"Recipient\": {\"AddressOwner\": \"0x849d63687330447431a2e76fecca4f3c10f6884ebaa9909674123c6c662612a3\"}}, null, null, true ]}" curl -X POST -H 'Content-type: application/json' --data-raw "$data" $rpc diff --git a/docs/content/references/iota-api.mdx b/docs/content/references/iota-api.mdx index 9fd2bb0fa22..ac1d15e9f42 100644 --- a/docs/content/references/iota-api.mdx +++ b/docs/content/references/iota-api.mdx @@ -2,13 +2,13 @@ title: IOTA RPC --- -[//]: # (:::info) +:::info -[//]: # () -[//]: # (The IOTA RPC is upgrading from JSON-RPC to GraphQL. See [GraphQL for IOTA RPC](../developer/_graphql-rpc.mdx) for more information.) -[//]: # () -[//]: # (:::) +The IOTA RPC is upgrading from JSON-RPC to GraphQL. See [GraphQL for IOTA RPC](../developer/graphql-rpc.mdx) for more information. + + +::: _IOTAJSON_ is a JSON-based format with restrictions that allow IOTA to align JSON inputs more closely with Move call arguments. diff --git a/docs/content/references/iota-evm/.gitignore b/docs/content/references/iota-evm/.gitignore deleted file mode 100644 index 31a1d0913e6..00000000000 --- a/docs/content/references/iota-evm/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -magic-contract/* -!magic-contract/introduction.md -iscutils diff --git a/docs/content/references/iota-evm/core-contracts/accounts.md b/docs/content/references/iota-evm/core-contracts/accounts.md deleted file mode 100644 index 4ddf76bf956..00000000000 --- a/docs/content/references/iota-evm/core-contracts/accounts.md +++ /dev/null @@ -1,387 +0,0 @@ ---- -description: 'The `accounts` contract keeps the ledger of on-chain accounts.' -image: /img/logo/WASP_logo_dark.png -tags: - - core-contract - - isc-accounts - - reference ---- - -# The `accounts` Contract - -The `accounts` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts -chain. - -This contract keeps a consistent ledger of on-chain accounts in its state, -i.e. [the L2 ledger](../../../developer/iota-evm/explanations/how-accounts-work.md). - ---- - -## Entry Points - -The `accounts` contract provides functions to deposit and withdraw tokens, information about the assets deposited on the -chain, and the functionality to create and use foundries. - -### `deposit()` - -A no-op that has the side effect of crediting any transferred tokens to the sender's account. - -:::note Gas Fees - -As with every call, the gas fee is debited from the L2 account right after executing the request. - -::: - -### `withdraw()` - -Moves tokens from the caller's on-chain account to the caller's L1 address. The number of -tokens to be withdrawn must be specified via the allowance of the request. - -:::note Contract Account - -Because contracts does not have a corresponding L1 address it does not make sense to -have them call this function. It will fail with an error. - -::: - -:::note Storage Deposit - -A call to withdraw means that a L1 output will be created. Because of this, the withdrawn -amount must be able to cover the L1 storage deposit. Otherwise, it will fail. - -::: - -### `transferAllowanceTo(a AgentID)` - -Transfers the specified allowance from the sender's L2 account to the given L2 account on -the chain. - -:::note - -When a transfer is made into an EVM account, an EVM tx will be created on the EVM side from the zero address (0x0000...) to the target account. -Information about what is being transferred will be encoded in the transaction's data using the following format: - -``` - + -``` - -The encoding used for this data can be found on [TIP-51](https://github.com/jorgemmsilva/tips/blob/b46d7bc36a0f7d4c2a1ad32ba25ec2abb4835cb3/tips/TIP-0051/tip-0051.md) - -::: - -#### Parameters - -- `a` (`AgentID`): The target L2 account. - -### `transferAccountToChain(g GasReserve)` - -Transfers the specified allowance from the sender SC's L2 account on -the target chain to the sender SC's L2 account on the origin chain. - -#### Parameters - -- `g` (`uint64`): Optional gas amount to reserve in the allowance for - the internal call to transferAllowanceTo(). Default 100 (MinGasFee). - -:::note Important Detailed Information - -[Read carefully before using this function.](xfer.md) - -::: - -### `nativeTokenCreate(t TokenScheme, tn TokenName, ts TokenSymbol, td TokenDecimal) s SerialNumber` - -Creates a new foundry and registers it as a ERC20 and IRC30 token. - -You can call this end point from the CLI using `wasp-cli chain create-native-token -h` - -#### Parameters - -- `t` ([`iotago::TokenScheme`](https://github.com/iotaledger/iota.go/blob/develop/token_scheme.go)): The token scheme - for the new foundry. -- `tn` (`string`): The token name -- `ts` (`string`): The token symbol -- `td` (`uint8`): The token decimals - -The storage deposit for the new foundry must be provided via allowance (only the minimum required will be used). - -#### Returns - -- `s` (`uint32`): The serial number of the newly created foundry - -### `nativeTokenModifySupply(s SerialNumber, d SupplyDeltaAbs, y DestroyTokens)` - -Mints or destroys tokens for the given foundry, which must be controlled by the caller. - -#### Parameters - -- `s` (`uint32`): The serial number of the foundry. -- `d` (positive `big.Int`): Amount to mint or destroy. -- `y` (optional `bool` - default: `false`): Whether to destroy tokens (`true`) or not (`false`). - -When minting new tokens, the storage deposit for the new output must be provided via an allowance. - -When destroying tokens, the tokens to be destroyed must be provided via an allowance. - -### `nativeTokenDestroy(s SerialNumber)` - -Destroys a given foundry output on L1, reimbursing the storage deposit to the caller. The foundry must be owned by the -caller. - -:::warning - -This operation cannot be reverted. - -::: - -#### Parameters - -- `s` (`uint32`): The serial number of the foundry. - -### `foundryCreateNew(t TokenScheme) s SerialNumber` - -:::warning Deprecated - -This function is deprecated, please use [`nativeTokenCreate`](#nativetokencreatet-tokenscheme-tn-tokenname-ts-tokensymbol-td-tokendecimal-s-serialnumber) instead - -::: - -Creates a new foundry with the specified token scheme, and assigns the foundry to the sender. - -You can call this end point from the CLI using `wasp-cli chain create-foundry -h` - -#### Parameters - -- `t` ([`iotago::TokenScheme`](https://github.com/iotaledger/iota.go/blob/develop/token_scheme.go)): The token scheme - for the new foundry. - -The storage deposit for the new foundry must be provided via allowance (only the minimum required will be used). - -#### Returns - -- `s` (`uint32`): The serial number of the newly created foundry - -### `mintNFT(I ImmutableData, a AgentID, C CollectionID, w WithdrawOnMint)` - -Mints an NFT with ImmutableData `I` that will be owned by the AgentID `a`. -It's possible to mint as part of a collection `C` (the caller must be the owner of the collection NFT to mint new NFTs as part of said collection). -The mint can be done directly to any L1 address (it is not necessary for the target to have an account on the chain) - -#### Parameters - -- `I` (`[]byte`): ImmutableData for the NFT. -- `a` (`AgentID`): AgentID that will be the owner of the NFT. -- `C` (optional `NFTID` - default empty): collectionID (NFTID) for the NFT. -- `w` (optional `bool` - default `false`): whether to withdrawal the NFT in the minting step (can only be `true` when the targetAgentID is a L1 address). - -#### Returns - -- `D` (`MintID`): the internal ID of the NFT at the time of minting that can be used by users/contracts to obtain the resulting NFTID on the next block - ---- - -## Views - -### `balance(a AgentID)` - -Returns the fungible tokens owned by the given Agent ID on the chain. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID. - -#### Returns - -A map of [`TokenID`](#tokenid) => `big.Int`. An empty token ID (a string of zero length) represents the L1 base token. - -### `balanceBaseToken(a AgentID)` - -Returns the amount of base tokens owned by any AgentID `a` on the chain. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID. - -#### Returns - -- `B` (`uint64`): The amount of base tokens in the account. - -### `balanceNativeToken(a AgentID, N TokenID)` - -Returns the amount of native tokens with Token ID `N` owned by any AgentID `a` on the chain. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID. -- `N` ([`TokenID`](#tokenid)): The Token ID. - -#### Returns - -- `B` (`big.Int`): The amount of native tokens in the account. - -### `totalAssets()` - -Returns the sum of all fungible tokens controlled by the chain. - -#### Returns - -A map of [`TokenID`](#tokenid) => `big.Int`. An empty token ID (a string of zero length) represents the L1 base token. - -### `accounts()` - -Returns a list of all agent IDs that own assets on the chain. - -#### Returns - -A map of `AgentiD` => `0x01`. - -### `getNativeTokenIDRegistry()` - -Returns a list of all native tokenIDs that are owned by the chain. - -#### Returns - -A map of [`TokenID`](#tokenid) => `0x01` - -### `nativeToken(s FoundrySerialNumber)` - -#### Parameters - -- `s` ([`FoundrySerialNumber`](#foundryserialnumber)): The Foundry serial number. - -#### Returns - -- `b`: [`iotago::FoundryOutput`](https://github.com/iotaledger/iota.go/blob/develop/output_foundry.go) - -### `accountNFTs(a AgentID)` - -Returns the NFT IDs for all NFTs owned by the given account. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID - -#### Returns - -- `i` ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) - of [`iotago::NFTID`](https://github.com/iotaledger/iota.go/blob/develop/output_nft.go)): - The NFT IDs owned by the account - -### `accountNFTAmount(a AgentID)` - -Returns the number of NFTs owned by the given account. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID - -#### Returns - -- `A` (`uint32`) Amount of NFTs owned by the account - -### `accountNFTsInCollection(a AgentID)` - -Returns the NFT IDs for all NFTs in the given collection that are owned by the given account. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID -- `C` (`NFTID`): The NFT ID of the collection - -#### Returns - -- `i` ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) - of [`iotago::NFTID`](https://github.com/iotaledger/iota.go/blob/develop/output_nft.go)): - The NFT IDs in the collection owned by the account - -### `accountNFTAmountInCollection(a AgentID)` - -Returns the number of NFTs in the given collection that are owned by the given account. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID -- `C` (`NFTID`): The NFT ID of the collection - -#### Returns - -- `A` (`uint32`) Amount of NFTs in the collection owned by the account - -### `accountFoundries(a AgentID)` - -Returns all foundries owned by the given account. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID - -#### Returns - -A map of [`FoundrySerialNumber`](#foundryserialnumber) => `0x01` - -### `nftData(z NFTID)` - -Returns the data for a given NFT with ID `z` that is on the chain. - -#### Returns - -- `e`: [`NFTData`](#nftdata) - -### `NFTIDbyMintID(D MintID)` - -Returns the NFTID `z` for a given MintID `D`. - -#### Parameters - -- `D` (`MintID`): MintID produced at the time the NFT was minted - -#### Returns - -- `z` (`NFTID`): The ID of the NFT - -### `getAccountNonce(a AgentID)` - -Returns the current account nonce for a give AgentID `a`. -The account nonce is used to issue off-ledger requests. - -#### Parameters - -- `a` (`AgentID`): The account Agent ID. - -#### Returns - -- `n` (`uint64`): The account nonce. - -## Schemas - -### `FoundrySerialNumber` - -``` -FoundrySerialNumber = uint32 -``` - -### `TokenID` - -``` -TokenID = [38]byte -``` - -### `NFTID` - -``` -NFTID = [32]byte -``` - -### `MintID` - -``` -MintID = [6]byte -``` - -### `NFTData` - -`NFTData` is encoded as the concatenation of: - -- The issuer ([`iotago::Address`](https://github.com/iotaledger/iota.go/blob/develop/address.go)). -- The NFT metadata: the length (`uint16`) followed by the data bytes. -- The NFT owner (`AgentID`). diff --git a/docs/content/references/iota-evm/core-contracts/blob.md b/docs/content/references/iota-evm/core-contracts/blob.md deleted file mode 100644 index 2bc0cbec425..00000000000 --- a/docs/content/references/iota-evm/core-contracts/blob.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -description: The `blobs` contract maintains a registry of _blobs_ (a collection of arbitrary binary data) referenced from smart contracts via their hashes. -image: /img/logo/WASP_logo_dark.png -tags: - - core-contract - - core-contract-blob - - reference ---- - -# The `blob` Contract - -The `blob` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts chain. - -The objective of the `blob` contract is to maintain an on-chain registry of _blobs_. -A blob is a collection of named chunks of binary data. - -``` -: -: -... -: -``` - -Both names and chunks are arbitrarily long byte slices. - -Blobs can be used to store arbitrary data like, for example, a collection of Wasm binaries needed to deploy a smart contract. - -Each blob in the registry is referenced by its hash. The hash is deterministically calculated from the concatenation of all pieces: - -``` -blobHash = hash( fieldName1 || binaryChunk1 || fieldName2 || binaryChunk2 || ... || fieldNameN || binaryChunkN) -``` - -Usually, field names are short strings, but their interpretation is use-case specific. - -Two predefined field names are interpreted by the VM while deploying smart contracts from binary: - -- _fieldname_ = `"v"` is interpreted as the _VM type_. -- _fieldname_ = `"p"` is interpreted as the _smart contract program binary_. - -If the field `"v"` is equal to the string `"wasmtime"`, the binary chunk of `"p"` is interpreted as WebAssembly binary, executable by the Wasmtime interpreter. - -The blob describing a smart contract may contain extra fields (ignored by the VM), for example: - -``` -"v" : VM type -"p" : smart contract program binary -"d" : data schema for data exchange between smart contract and outside sources and consumers -"s" : program sources in .zip format -``` - ---- - -## Entry Points - -### `storeBlob()` - -Stores a new blob in the registry. - -#### Parameters - -The key/value pairs of the received parameters are interpreted as the field/chunk pairs of the blob. - -#### Returns - -- `hash` (`[32]byte`): The hash of the stored blob. - ---- - -## Views - -### `getBlobInfo(hash BlobHash)` - -Returns the size of each chunk of the blob. - -#### Parameters - -- `hash` (`[32]byte`): The hash of the blob. - -#### Returns - -``` -: (uint32) -... -: (uint32) -``` - -### `getBlobField(hash BlobHash, field BlobField)` - -Returns the chunk associated with the given blob field name. - -#### Parameters - -- `hash` (`[32]byte`): The hash of the blob. -- `field` (`[]byte`): The field name. - -#### Returns - -- `bytes` (`[]byte`): The chunk associated with the given field name. - -### `listBlobs()` - -Returns a list of pairs `blob hash`: `total size of chunks` (`uint32`) for all blobs in the registry. diff --git a/docs/content/references/iota-evm/core-contracts/blocklog.md b/docs/content/references/iota-evm/core-contracts/blocklog.md deleted file mode 100644 index c1e7e22dc17..00000000000 --- a/docs/content/references/iota-evm/core-contracts/blocklog.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -description: The `blocklog` contract keeps track of the blocks of requests processed by the chain. -image: /img/logo/WASP_logo_dark.png -tags: - - core-contract - - core-contract-blocklog - - reference ---- - -# The `blocklog` Contract - -The `blocklog` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts chain. - -The `blocklog` contract keeps track of the blocks of requests processed by the chain, providing views to get request -status, receipts, block, and event details. - -To avoid having a monotonically increasing state size, only the latest `N` -blocks (and their events and receipts) are stored. This parameter can be configured -when deploying the chain. - ---- - -## Entry Points - -### `retryUnprocessable(u requestID)` - -Tries to retry a given request that was marked as "unprocessable". - -:::note -"Unprocessable" requests are on-ledger requests that do not include enough base tokens to cover the deposit fees (example if an user tries to deposit many native tokens in a single output but only includes the minimum possible amount of base tokens). Such requests will be collected into an "unprocessable list" and users are able to deposit more funds onto their on-chain account and retry them afterwards. -::: - -#### Parameters - -- `u` ([`isc::RequestID`](https://github.com/iotaledger/wasp/blob/develop/packages/isc/request.go)): The requestID to be retried. (sender of the retry request must match the sender of the "unprocessable" request) - ---- - -## Views - -### `getBlockInfo(n uint32)` - -Returns information about the block with index `n`. - -#### Parameters - -- `n`: (optional `uint32`) The block index. Default: the latest block. - -#### Returns - -- `n` (`uint32`):The block index. -- `i` ([`BlockInfo`](#blockinfo)):The information about the block. - -### `getRequestIDsForBlock(n uint32)` - -Returns a list with all request IDs in the block with block index `n`. - -#### Parameters - -- `n` (optional `uint32`):The block index. The default value is the latest block. - -#### Returns - -- `n` (`uint32`):The block index. -- `u`: ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) - of [`RequestID`](#requestid)) - -### `getRequestReceipt(u RequestID)` - -Returns the receipt for the request with the given ID. - -#### Parameters - -- `u` ([`RequestID`](#requestid)):The request ID. - -#### Returns - -- `n` (`uint32`):The block index. -- `r` (`uint16`):The request index within the block. -- `d` ([`RequestReceipt`](#requestreceipt)):The request receipt. - -### `getRequestReceiptsForBlock(n uint32)` - -Returns all the receipts in the block with index `n`. - -#### Parameters - -- `n` (optional `uint32`):The block index. Defaults to the latest block. - -#### Returns - -- `n` (`uint32`):The block index. -- `d`: ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) - of [`RequestReceipt`](#requestreceipt)) - -### `isRequestProcessed(u RequestID)` - -Returns whether the request with ID `u` has been processed. - -#### Parameters - -- `u` ([`RequestID`](#requestid)):The request ID. - -#### Returns - -- `p` (`bool`):Whether the request was processed or not. - -### `getEventsForRequest(u RequestID)` - -Returns the list of events triggered during the execution of the request with ID `u`. - -### Parameters - -- `u` ([`RequestID`](#requestid)):The request ID. - -#### Returns - -- `e`: ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) of `[]byte`). - -### `getEventsForBlock(n blockIndex)` - -Returns the list of events triggered during the execution of all requests in the block with index `n`. - -#### Parameters - -- `n` (optional `uint32`):The block index. Defaults to the latest block. - -#### Returns - -- `e`: ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) of `[]byte`). - -### `getEventsForContract(h Hname)` - -Returns a list of events triggered by the smart contract with hname `h`. - -#### Parameters - -- `h` (`hname`):The smart contract’s hname. -- `f` (optional `uint32` - default: `0`):"From" block index. -- `t` (optional `uint32` - default: `MaxUint32`):"To" block index. - -#### Returns - -- `e`: ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) of `[]byte`) - -### `hasUnprocessable(u requestID)` - -Asserts whether or not a given requestID (`u`) is present in the "unprocessable list" - -#### Parameters - -- `u` ([`isc::RequestID`](https://github.com/iotaledger/wasp/blob/develop/packages/isc/request.go)): The requestID to be checked - -#### Returns - -- `x` ([`bool`]) Whether or not the request exists in the "unprocessable list" - ---- - -## Schemas - -### `RequestID` - -A `RequestID` is encoded as the concatenation of: - -- Transaction ID (`[32]byte`). -- Transaction output index (`uint16`). - -### `BlockInfo` - -`BlockInfo` is encoded as the concatenation of: - -- The block timestamp (`uint64` UNIX nanoseconds). -- Amount of requests in the block (`uint16`). -- Amount of successful requests (`uint16`). -- Amount of off-ledger requests (`uint16`). -- Anchor transaction ID ([`iotago::TransactionID`](https://github.com/iotaledger/iota.go/blob/develop/transaction.go)). -- Anchor transaction sub-essence hash (`[32]byte`). -- Previous L1 commitment (except for block index 0). - - Trie root (`[20]byte`). - - Block hash (`[20]byte`). -- Total base tokens in L2 accounts (`uint64`). -- Total storage deposit (`uint64`). -- Gas burned (`uint64`). -- Gas fee charged (`uint64`). - -### `RequestReceipt` - -`RequestReceipt` is encoded as the concatenation of: - -- Gas budget (`uint64`). -- Gas burned (`uint64`). -- Gas fee charged (`uint64`). -- The request ([`isc::Request`](https://github.com/iotaledger/wasp/blob/develop/packages/isc/request.go)). -- Whether the request produced an error (`bool`). -- If the request produced an error, the - [`UnresolvedVMError`](./errors.md#unresolvedvmerror). diff --git a/docs/content/references/iota-evm/core-contracts/errors.md b/docs/content/references/iota-evm/core-contracts/errors.md deleted file mode 100644 index 7d8141c3e28..00000000000 --- a/docs/content/references/iota-evm/core-contracts/errors.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -description: 'The errors contract keeps a map of error codes to error message templates. These error codes are used in -request receipts.' -image: /img/logo/WASP_logo_dark.png -tags: -- core-contract -- core-contract-errors -- reference ---- - -# The `errors` Contract - -The `errors` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts -chain. - -The `errors` contract keeps a map of error codes to error message templates. -This allows contracts to store lengthy error strings only once and then reuse them by just providing the error code (and -optional extra values) when producing an error, thus saving storage and gas. - ---- - -## Entry Points - -### `registerError(m ErrorMessageFormat) c ErrorCode` - -Registers an error message template. - -#### Parameters - -- `m` (`string`): The error message template, which supports standard [go verbs](https://pkg.go.dev/fmt#hdr-Printing) - for variable printing. - -#### Returns - -- `c` (`ErrorCode`): The error code of the registered template - ---- - -## Views - -### `getErrorMessageFormat(c ErrorCode) m ErrorMessageFormat` - -Returns the message template stored for a given error code. - -#### Parameters - -- `c` (`ErrorCode`): The error code of the registered template. - -#### Returns - -- `m` (`string`): The error message template. - ---- - -## Schemas - -### `ErrorCode` - -`ErrorCode` is encoded as the concatenation of: - -- The contract hname(`hname`). -- The error ID, calculated as the hash of the error template(`uint16`). - -### `UnresolvedVMError` - -`UnresolvedVMError` is encoded as the concatenation of: - -- The error code ([`ErrorCode`](#errorcode)). -- CRC32 checksum of the formatted string (`uint32`). -- The JSON-encoded list of parameters for the template (`string` prefixed with `uint16` size). diff --git a/docs/content/references/iota-evm/core-contracts/evm.md b/docs/content/references/iota-evm/core-contracts/evm.md deleted file mode 100644 index f0918bc47d6..00000000000 --- a/docs/content/references/iota-evm/core-contracts/evm.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -description: 'The evm core contract provides the necessary infrastructure to accept Ethereum transactions and execute -EVM code.' -image: /img/logo/WASP_logo_dark.png -tags: -- core-contract -- evm -- reference ---- - -# The `evm` Contract - -The `evm` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts chain. - -The `evm` core contract provides the necessary infrastructure to accept Ethereum transactions and execute EVM code. -It also includes the implementation of the [ISC Magic contract](../../../developer/iota-evm/how-tos/core-contracts/introduction.md). - -:::note - -For more information about how ISC supports EVM contracts, refer to the [EVM](../../../developer/iota-evm/getting-started/languages-and-vms.mdx#what-is-evmsolidity) section. - -::: - ---- - -## Entry Points - -Most entry points of the `evm` core contract are meant to be accessed through the JSON-RPC service provided -automatically by the Wasp node so that the end users can use standard EVM tools like [MetaMask](https://metamask.io/). -We only list the entry points not exposed through the JSON-RPC interface in this document. - -### `init()` - -Called automatically when the ISC is deployed. - -Some parameters of the `evm` contract can be specified by passing them to the -[`root` contract `init` entry point](root.md#init): - -- `evmg` (optional [`GenesisAlloc`](#genesisalloc)): The genesis allocation. The balance of all accounts must be 0. -- `evmbk` (optional `int32` - default: keep all): Amount of EVM blocks to keep in the state. -- `evmchid` (optional `uint16` - default: 1074): EVM chain iD - - :::caution - - Re-using an existing Chain ID is not recommended and can be a security risk. For serious usage, register a unique - Chain ID on [Chainlist](https://chainlist.org/) and use that instead of the default. **It is not possible to change - the EVM chain ID after deployment.** - - ::: - -- `evmw` (optional [`GasRatio`](./governance.md#ratio32) - default: `1:1`): The ISC to EVM gas ratio. - -### `registerERC20NativeToken` - -Registers an ERC20 contract to act as a proxy for the native tokens, at address -`0x107402xxxxxxxx00000000000000000000000000`, where `xxxxxxxx` is the -little-endian encoding of the foundry serial number. - -Only the foundry owner can call this endpoint. - -#### Parameters - -- `fs` (`uint32`): The foundry serial number -- `n` (`string`): The token name -- `t` (`string`): The ticker symbol -- `d` (`uint8`): The token decimals - -You can call this endpoint with the `wasp-cli register-erc20-native-token` command. See -`wasp-cli chain register-erc20-native-token -h` for instructions on how to use the command. - -### `registerERC20NativeTokenOnRemoteChain` - -Registers an ERC20 contract to act as a proxy for the native tokens **on another -chain**. - -The foundry must be controlled by this chain. Only the foundry owner can call -this endpoint. - -This endpoint is intended to be used in case the foundry is controlled by chain -A, and the owner of the foundry wishes to register the ERC20 contract on chain -B. In that case, the owner must call this endpoint on chain A with `target = chain B`. -The request to chain B is then sent as an on-ledger request. -After a few minutes, call -[`getERC20ExternalNativeTokenAddress`](#geterc20externalnativetokenaddress) -on chain B to find out the address of the ERC20 contract. - -#### Parameters - -- `fs` (`uint32`): The foundry serial number -- `n` (`string`): The token name -- `t` (`string`): The ticker symbol -- `d` (`uint8`): The token decimals -- `A` (`uint8`): The target chain address, where the ERC20 contract will be - registered. - -You can call this endpoint with the `wasp-cli register-erc20-native-token-on-remote-chain` command. See -`wasp-cli chain register-erc20-native-token-on-remote-chain -h` for instructions on how to use the command. - -### `registerERC20ExternalNativeToken` - -Registers an ERC20 contract to act as a proxy for the native tokens. - -Only an alias address can call this endpoint. - -If the foundry is controlled by another ISC chain, the foundry owner can call -[`registerERC20NativeTokenOnRemoteChain`](#registererc20nativetokenonremotechain) -on that chain, which will automatically call this endpoint on the chain set as -target. - -#### Parameters - -- `fs` (`uint32`): The foundry serial number -- `n` (`string`): The token name -- `t` (`string`): The ticker symbol -- `d` (`uint8`): The token decimals -- `T` (`TokenScheme`): The native token scheme - -### `registerERC721NFTCollection` - -Registers an ERC721 contract to act as a proxy for an NFT collection, at address -`0x107404xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`, where `xxx...` is the first 17 -bytes of the collection ID. - -The call will fail if the address is taken by another collection with the same prefix. - -#### Parameters - -- `C` (`NTFID`): The collection ID - ---- - -## Views - -### `getERC20ExternalNativeTokenAddress` - -Returns the address of an ERC20 contract registered with -[`registerERC20NativeTokenOnRemoteChain`](#registererc20nativetokenonremotechain). - -Only the foundry owner can call this endpoint. - -#### Parameters - -- `N` (`NativeTokenID`): The native token ID - ---- - -## Schemas - -### `GenesisAlloc` - -`GenesisAlloc` is encoded as the concatenation of: - -- Amount of accounts `n` (`uint32`). -- `n` times: - - Ethereum address (`[]byte` prefixed with `uint32` size). - - Account code (`[]byte` prefixed with `uint32` size). - - Amount of storage key/value pairs `m`(`uint32`). - - `m` times: - - Key (`[]byte` prefixed with `uint32` size). - - Value(`[]byte` prefixed with `uint32` size). - - Account balance (must be 0)(`[]byte` prefixed with `uint32` size). - - Account nonce (`uint64`). - - Account private key (may be used for tests)(`uint64`). diff --git a/docs/content/references/iota-evm/core-contracts/governance.md b/docs/content/references/iota-evm/core-contracts/governance.md deleted file mode 100644 index 398d06f003b..00000000000 --- a/docs/content/references/iota-evm/core-contracts/governance.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -description: 'The `governance` contract defines the set of identities that constitute the state controller, access nodes, -who is the chain owner, and the fees for request execution.' -image: /img/logo/WASP_logo_dark.png -tags: -- core-contract -- core-contract-governance -- reference ---- - -# The `governance` Contract - -The `governance` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts -chain. - -The `governance` contract provides the following functionalities: - -- It defines the identity set that constitutes the state controller (the entity that owns the state output via the chain - Alias Address). It is possible to add/remove addresses from the state controller (thus rotating the committee of - validators). -- It defines the chain owner (the L1 entity that owns the chain - initially whoever deployed it). The chain owner can - collect special fees and customize some chain-specific parameters. -- It defines the entities allowed to have an access node. -- It defines the fee policy for the chain (gas price, what token is used to pay for gas, and the validator fee share). - ---- - -## Fee Policy - -The Fee Policy looks like the following: - -```go -{ - GasPerToken Ratio32 // how many gas units are paid for each token - EVMGasRatio Ratio32 // the ratio at which EVM gas is converted to ISC gas - ValidatorFeeShare uint8 // percentage of the fees that are credited to the validators (0 - 100) -} -``` - ---- - -## Entry Points - -### `rotateStateController(S StateControllerAddress)` - -Called when the committee is about to be rotated to the new address `S`. - -If it succeeds, the next state transition will become a governance transition, thus updating the state controller in the -chain's Alias Output. If it fails, nothing happens. - -It can only be invoked by the chain owner. - -#### Parameters - -- `S` ([`iotago::Address`](https://github.com/iotaledger/iota.go/blob/develop/address.go)): The address of the next - state controller. Must be an - [allowed](#addallowedstatecontrolleraddresss-statecontrolleraddress) state controller address. - -### `addAllowedStateControllerAddress(S StateControllerAddress)` - -Adds the address `S` to the list of identities that constitute the state controller. - -It can only be invoked by the chain owner. - -#### Parameters - -- `S` ([`iotago::Address`](https://github.com/iotaledger/iota.go/blob/develop/address.go)): The address to add to the - set of allowed state controllers. - -### `removeAllowedStateControllerAddress(S StateControllerAddress)` - -Removes the address `S` from the list of identities that constitute the state controller. - -It can only be invoked by the chain owner. - -#### Parameters - -- `S` ([`iotago::Address`](https://github.com/iotaledger/iota.go/blob/develop/address.go)): The address to remove from - the set of allowed state controllers. - -### `delegateChainOwnership(o AgentID)` - -Sets the Agent ID `o` as the new owner for the chain. This change will only be effective -once [`claimChainOwnership`](#claimchainownership) is called by `o`. - -It can only be invoked by the chain owner. - -#### Parameters - -- `o` (`AgentID`): The Agent ID of the next chain owner. - -### `claimChainOwnership()` - -Claims the ownership of the chain if the caller matches the identity set -in [`delegateChainOwnership`](#delegatechainownershipo-agentid). - -### `setFeePolicy(g FeePolicy)` - -Sets the fee policy for the chain. - -#### Parameters - -- `g`: ([`FeePolicy`](#feepolicy)). - -It can only be invoked by the chain owner. - -### `setGasLimits(l GasLimits)` - -Sets the gas limits for the chain. - -#### Parameters - -- `l`: ([`GasLimits`](#gaslimits)). - -It can only be invoked by the chain owner. - -### `setEVMGasRatio(e Ratio32)` - -Sets the EVM gas ratio for the chain. - -#### Parameters - -- `e` ([`Ratio32`](#ratio32)): The EVM gas ratio. - -It can only be invoked by the chain owner. - -### `addCandidateNode(ip PubKey, ic Certificate, ia API, i ForCommittee)` - -Adds a node to the list of candidates. - -#### Parameters - -- `ip` (`[]byte`): The public key of the node to be added. -- `ic` (`[]byte`): The certificate is a signed binary containing both the node public key and their L1 address. -- `ia` (`string`): The API base URL for the node. -- `i` (optional `bool` - default: `false`): Whether the candidate node is being added to be part of the committee or - just an access node. - -It can only be invoked by the access node owner (verified via the Certificate field). - -### `revokeAccessNode(ip PubKey, ic Certificate, ia API, i ForCommittee)` - -Removes a node from the list of candidates. - -#### Parameters - -- `ip` (`[]byte`): The public key of the node to be removed. -- `ic` (`[]byte`): The certificate of the node to be removed. - -It can only be invoked by the access node owner (verified via the Certificate field). - -### `changeAccessNodes(n actions)` - -Iterates through the given map of actions and applies them. - -#### Parameters - -- `n` ([`Map`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/map.go) of `public key` => `byte`): - The list of actions to perform. Each byte value can be one of the following: - - `0`: Remove the access node from the access nodes list. - - `1`: Accept a candidate node and add it to the list of access nodes. - - `2`: Drop an access node from the access node and candidate lists. - -It can only be invoked by the chain owner. - -### `startMaintenance()` - -Starts the chain maintenance mode, meaning no further requests will be processed except -calls to the governance contract. - -It can only be invoked by the chain owner. - -### `stopMaintenance()` - -Stops the maintenance mode. - -It can only be invoked by the chain owner. - -### `setCustomMetadata(x bytes)` - -Changes optional extra metadata that is appended to the L1 AliasOutput. - -#### Parameters - -- `x` (`bytes`): the optional metadata - -### `setPayoutAgentID` - -`setPayoutAgentID` sets the payout AgentID. The default AgentID is the chain owner. Transaction fee will be taken to ensure the common account has minimum storage deposit which is in base token. The rest of transaction fee will be transferred to payout AgentID. - -#### Parameters - -- `s` (`AgentID`): the payout AgentID - -### `setMinCommonAccountBalance` - -`setMinCommonAccountBalance` sets the minimum balanced to be held in the common account. - -#### Parameters - -- `ms` (`AgentID`): the minimum common account balance - ---- - -## Views - -### `getAllowedStateControllerAddresses()` - -Returns the list of allowed state controllers. - -#### Returns - -- `a` ([`Array`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/array.go) - of [`iotago::Address`](https://github.com/iotaledger/iota.go/blob/develop/address.go)): The list of allowed state - controllers. - -### `getChainOwner()` - -Returns the AgentID of the chain owner. - -#### Returns - -- `o` (`AgentID`): The chain owner. - -### `getChainInfo()` - -Returns information about the chain. - -#### Returns - -- `c` (`ChainID`): The chain ID -- `o` (`AgentID`): The chain owner -- `g` ([`FeePolicy`](#feepolicy)): The gas fee policy -- `l` ([`GasLimits`](#gaslimits)): The gas limits -- `x` (`bytes`): The custom metadata - -### `getFeePolicy()` - -Returns the gas fee policy. - -#### Returns - -- `g` ([`FeePolicy`](#feepolicy)): The gas fee policy. - -### `getEVMGasRatio` - -Returns the ISC : EVM gas ratio. - -#### Returns - -- `e` ([`Ratio32`](#ratio32)): The ISC : EVM gas ratio. - -### `getGasLimits()` - -Returns the gas limits. - -#### Returns - -- `l` ([`GasLimits`](#gaslimits)): The gas limits. - -### `getChainNodes()` - -Returns the current access nodes and candidates. - -#### Returns - -- `ac` ([`Map`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/map.go) - of public key => `0x01`): The access nodes. -- `an` ([`Map`](https://github.com/iotaledger/wasp/blob/develop/packages/kv/collections/map.go) - of public key => [`AccessNodeInfo`](#accessnodeinfo)): The candidate nodes. - -### `getMaintenanceStatus()` - -Returns whether the chain is undergoing maintenance. - -#### Returns - -- `m` (`bool`): `true` if the chain is in maintenance mode - -### `getCustomMetadata()` - -Returns the extra metadata that is added to the chain AliasOutput. - -#### Returns - -- `x` (`bytes`): the optional metadata - -### `getPayoutAgentID` - -`getPayoutAgentID` gets the payout AgentID. - -Returns the payout AgentID of the chain. - -#### Returns - -- `s` (`AgentID`): the payout AgentID. - -### `getMinCommonAccountBalance` - -`getMinCommonAccountBalance` returns the minimum balanced to be held in the common account. - -#### Returns - -- `ms` (`uint64`): the minimum storage deposit. - -## Schemas - -### `Ratio32` - -A ratio between two values `x` and `y`, expressed as two `int32` numbers `a:b`, where `y = x * b/a`. - -`Ratio32` is encoded as the concatenation of the two `uint32` values `a` & `b`. - -### `FeePolicy` - -`FeePolicy` is encoded as the concatenation of: - -- The [`TokenID`](accounts.md#tokenid) of the token used to charge for gas. (`iotago.NativeTokenID`) - - If this value is `nil`, the gas fee token is the base token. -- Gas per token ([`Ratio32`](#ratio32)): expressed as an `a:b` (`gas/token`) ratio, meaning how many gas units each token pays for. -- Validator fee share. Must be between 0 and 100, meaning the percentage of the gas fees distributed to the - validators. (`uint8`) -- The ISC:EVM gas ratio ([`Ratio32`](#ratio32)): such that `ISC gas = EVM gas * a/b`. - -### `GasLimits` - -`GasLimits` is encoded as the concatenation of: - -- The maximum gas per block (`uint64`). A request that exceeds this limit is - skipped and processed in the next block. -- The minimum gas per request (`uint64`). If a request consumes less than this - value, it is charged for this instead. -- The maximum gas per request (`uint64`). If a request exceeds this limit, it - is rejected as failed. -- The maximum gas per external view call (`uint64). This is the gas budget - assigned to external view calls. - -### `AccessNodeInfo` - -`AccessNodeInfo` is encoded as the concatenation of: - -- The validator address. (`[]byte` prefixed by `uint16` size) -- The certificate. (`[]byte` prefixed by `uint16` size) -- Whether the access node is part of the committee of validators. (`bool`) -- The API base URL. (`string` prefixed by `uint16` size) diff --git a/docs/content/references/iota-evm/core-contracts/overview.md b/docs/content/references/iota-evm/core-contracts/overview.md deleted file mode 100644 index 601e509920e..00000000000 --- a/docs/content/references/iota-evm/core-contracts/overview.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: There currently are 6 core smart contracts that are always deployed on each chain, root, _default, accounts, blob, blocklog, and governance. -image: /img/banner/banner_wasp_core_contracts_overview.png -tags: - - core-contract - - reference ---- - -# Core Contracts - -![Wasp Node Core Contracts Overview](/img/banner/banner_wasp_core_contracts_overview.png) - -There are currently 7 core smart contracts that are always deployed on each -chain. These are responsible for the vital functions of the chain and -provide infrastructure for all other smart contracts: - -- [`root`](./root.md): Responsible for the initialization of the chain, maintains registry of deployed contracts. - -- [`accounts`](./accounts.md): Manages the on-chain ledger of accounts. - -- [`blob`](./blob.md): Responsible for the registry of binary objects of arbitrary size. - -- [`blocklog`](./blocklog.md): Keeps track of the blocks and receipts of requests that were processed by the chain. - -- [`governance`](./governance.md): Handles the administrative functions of the chain. For example: rotation of the committee of validators of the chain, fees and other chain-specific configurations. - -- [`errors`](./errors.md): Keeps a map of error codes to error messages templates. These error codes are used in request receipts. - -- [`evm`](./evm.md): Provides the necessary infrastructure to accept Ethereum - transactions and execute EVM code. diff --git a/docs/content/references/iota-evm/core-contracts/root.md b/docs/content/references/iota-evm/core-contracts/root.md deleted file mode 100644 index 09450bfd98b..00000000000 --- a/docs/content/references/iota-evm/core-contracts/root.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -description: 'The root contract is the first smart contract deployed on the chain. It functions as a smart contract -factory for the chain.' -image: /img/logo/WASP_logo_dark.png -tags: -- core-contract -- core-contract-root -- reference ---- - -# The `root` Contract - -The `root` contract is one of the [core contracts](overview.md) on each IOTA Smart Contracts -chain. - -The `root` contract is responsible for the initialization of the chain. -It is the first smart contract deployed on the chain and, upon receiving the `init` request, bootstraps the state of the -chain. -Deploying all of the other core contracts is a part of the state initialization. - -The `root` contract also functions as a smart contract factory for the chain: upon request, it deploys other smart -contracts and maintains an on-chain registry of smart contracts in its state. -The contract registry keeps a list of contract records containing their respective name, hname, description, and -creator. - ---- - -## Entry Points - -### `init()` - -The constructor. Automatically called immediately after confirmation of the origin transaction and never called again. -When executed, this function: - -- Initializes base values of the chain according to parameters. -- Sets the caller as the _chain owner_. -- Deploys all the core contracts. - -### `deployContract(ph ProgramHash, nm Name, ds Description)` - -Deploys a non-EVM smart contract on the chain if the caller has deployment permission. - -#### Parameters - -- `ph` (`[32]byte`): The hash of the binary _blob_ (that has been previously stored in the [`blob` contract](blob.md)). -- `nm` (`string`): The name of the contract to be deployed, used to calculate the - contract's _hname_. The hname must be unique among all contract hnames in the chain. -- `ds` (`string`): Description of the contract to be deployed. - -Any other parameters that are passed to the deployContract function will be passed on to -the `init` function of the smart contract being deployed. Note that this means that the -init parameter names cannot be the above ones, as they will have been filtered out. - -### `grantDeployPermission(dp AgentID)` - -The chain owner grants deploy permission to the agent ID `dp`. - -#### Parameters - -`dp`(AgentID): The agent ID. - -### `revokeDeployPermission(dp AgentID)` - -The chain owner revokes the deploy permission of the agent ID `dp`. - -#### Parameters - -`dp`(AgentID): The agent ID. - -### `requireDeployPermissions(de DeployPermissionsEnabled)` - -#### Parameters - -- `de` (`bool`): Whether permissions should be required to deploy a contract on the chain. - -By default, permissions are enabled (addresses need to be granted the right to deploy), but the chain owner can override -this setting to allow anyone to deploy contracts on the chain. - ---- - -## Views - -### `findContract(hn Hname)` - -Returns the record for a given smart contract with Hname `hn` (if it exists). - -#### Parameters - -`hn`: The smart contract’s Hname - -#### Returns - -- `cf` (`bool`): Whether or not the contract exists. -- `dt` ([`ContractRecord`](#contractrecord)): The requested contract record (if it exists). - -### `getContractRecords()` - -Returns the list of all smart contracts deployed on the chain and related records. - -#### Returns - -A map of `Hname` => [`ContractRecord`](#contractrecord) - ---- - -## Schemas - -### `ContractRecord` - -A `ContractRecord` is encoded as the concatenation of: - -- Program hash (`[32]byte`). -- Contract description (`string`). -- Contract name (`string`). diff --git a/docs/content/references/iota-evm/core-contracts/xfer.md b/docs/content/references/iota-evm/core-contracts/xfer.md deleted file mode 100644 index dc0affd0dce..00000000000 --- a/docs/content/references/iota-evm/core-contracts/xfer.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: 'The `transferAccountToChain` contract needs special consideration.' -image: /img/logo/WASP_logo_dark.png -tags: - - core-contract - - reference ---- - -# `accounts.transferAccountToChain` - -The `transferAccountToChain` function of the `accounts` contract is one that needs -careful consideration before use. Make sure you understand precisely how to use it to -prevent - ---- - -## Entry Point - -### `transferAccountToChain(g GasReserve)` - -Transfers the specified allowance from the sender SC's L2 account on -the target chain to the sender SC's L2 account on the origin chain. - -Caller must be a contract, and we will transfer the allowance from its L2 account -on the target chain to its L2 account on the origin chain. This requires that -this function takes the allowance into custody and in turn sends the assets as -allowance to the origin chain, where that chain's accounts.TransferAllowanceTo() -function then transfers it into the caller's L2 account on that chain. - -#### Parameters - -- `g` (`uint64`): Optional gas amount to reserve in the allowance for - the internal call to transferAllowanceTo(). Default 100 (MinGasFee). - But better to provide it so that it matches the fee structure. - -### IMPORTANT CONSIDERATIONS - -1. The caller contract needs to provide sufficient base tokens in its - allowance, to cover the gas fee GAS1 for this request. - Note that this amount depends on the fee structure of the target chain, - which can be different from the fee structure of the caller's own chain. - -2. The caller contract also needs to provide sufficient base tokens in - its allowance, to cover the gas fee GAS2 for the resulting request to - accounts.TransferAllowanceTo() on the origin chain. The caller needs to - also specify this GAS2 amount through the GasReserve parameter. - -3. The caller contract also needs to provide a storage deposit SD with - this request, holding enough base tokens _independent_ of the GAS1 and - GAS2 amounts. - Since this storage deposit is dictated by L1 we can use this amount as - storage deposit for the resulting accounts.TransferAllowanceTo() request, - where it will then be returned to the caller as part of the transfer. - -4. This means that the caller contract needs to provide at least - GAS1 + GAS2 + SD base tokens as assets to this request, and provide an - allowance to the request that is exactly GAS2 + SD + transfer amount. - Failure to meet these conditions may result in a failed request and - worst case the assets sent to accounts.TransferAllowanceTo() could be - irretrievably locked up in an account on the origin chain that belongs - to the accounts core contract of the target chain. - -5. The caller contract needs to set the gas budget for this request to - GAS1 to guard against unanticipated changes in the fee structure that - raise the gas price, otherwise the request could accidentally cannibalize - GAS2 or even SD, with potential failure and locked up assets as a result. diff --git a/docs/content/references/iota-evm/json-rpc-spec.md b/docs/content/references/iota-evm/json-rpc-spec.md deleted file mode 100644 index 82d14e491a0..00000000000 --- a/docs/content/references/iota-evm/json-rpc-spec.md +++ /dev/null @@ -1,64 +0,0 @@ -# JSON-RPC API - -The [JSON-RPC](https://www.jsonrpc.org/specification) is a stateless, lightweight remote procedure call (RPC) protocol. It defines several data structures and the rules around their processing. It is transport agnostic because the concepts can be used within the same process, over sockets, HTTP, or in various message-passing environments. It uses JSON (RFC 4627) as data format. - -This page deals with the JSON-RPC API used by EVM execution clients. - -## JSON-RPC Methods According to [Ethereum Client API](https://ethereum.org/en/developers/docs/apis/json-rpc/) - -| Method | Description | Status | -| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| [eth_getBlockByHash](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbyhash) | _Returns information about a block by hash_ | ✅ | -| [eth_getBlockByNumber](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) | _Returns information about a block by number_ | ✅ | -| [eth_getBlockTransactionCountByHash](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblocktransactioncountbyhash) | _Returns the number of transactions in a block from a block matching the given block hash_ | ✅ | -| [eth_getBlockTransactionCountByNumber](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblocktransactioncountbynumber) | _Returns the number of transactions in a block matching the given block number_ | ✅ | -| [eth_getUncleCountByBlockHash](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclecountbyblockhash) | _Returns the number of uncles in a block from a block matching the given block hash_ | ✅ | -| [eth_getUncleCountByBlockNumber](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getunclecountbyblocknumber) | _Returns the number of uncles in a block from a block matching the given block number_ | ✅ | -| [eth_protocolVersion](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_protocolversion) | _Returns the current Ethereum protocol version_ | ✅ | -| [eth_chainId](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_chainid) | _Returns the chain ID of the current network_ | ✅ | -| [eth_syncing](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_syncing) | _Returns an object with data about the sync status or false-copy_ | ✅ | -| [eth_coinbase](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_coinbase) | _Returns the client Coinbase address_ | ✅ | -| [eth_accounts](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_accounts) | _Returns a list of addresses owned by client_ | ✅ | -| [eth_blockNumber](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber) | _Returns the number of most recent block._ | ✅ | -| [eth_call](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call) | _Executes a new message call immediately without creating a transaction on the blockchain_ | ✅ | -| [eth_estimateGas](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_estimategas) | _Generates and returns an estimate of how much gas is necessary to allow the transaction to complete._ | ✅ | -| [eth_gasPrice](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gasprice) | _Returns the current price per gas in wei_ | ✅ | -| [eth_feeHistory](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_feehistory) | _Returns fee history_ | ✅ | -| [eth_newFilter](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newfilter) | _Creates a filter object, based on filter options, to notify when the state changes (logs)_ | ❌ | -| [eth_newBlockFilter](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newblockfilter) | _Creates a filter in the node, to notify when a new block arrives_ | ❌ | -| [eth_newPendingTransactionFilter](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newpendingtransactionfilter) | _Creates a filter in the node, to notify when new pending transactions arrive_ | ❌ | -| [eth_uninstallFilter](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_uninstallfilter) | _Uninstalls a filter with given id_ | ❌ | -| [eth_getFilterChanges](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterchanges) | _Polling method for a filter, which returns an array of logs which occurred since last poll_ | ❌ | -| [eth_getFilterLogs](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterlogs) | _Returns an array of all logs matching filter with given id. Can compute the same results with an `eth_getLogs call`_ | ❌ | -| [eth_getLogs](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getlogs) | _Anytime a transaction is mined, we can see event logs for that transaction by making a request to eth_getLogs and then take actions based off those results_ | ✅ | -| [eth_mining](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_mining) | _Returns whether the client is actively mining new blocks_ | ✅ | -| [eth_hashrate](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_hashrate) | _Returns the number of hashes per second that the node is mining with_ | ✅ | -| [eth_sign](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) | _Returns an EIP-191 signature over the provided data._ | ✅ | -| [eth_signTransaction](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction) | _Signs and submits a transaction_ | ✅ | -| [eth_getBalance](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getbalance) | _Returns the balance of the account of given address_ | ✅ | -| [eth_getStorageAt](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getstorageat) | _Returns the value from a storage position at a given address_ | ✅ | -| [eth_getTransactionCount](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactioncount) | _Returns the number of transactions sent from an address_ | ✅ | -| [eth_getCode](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getcode) | _Returns code at a given address_ | ✅ | -| [eth_sendTransaction](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction) | _Signs and submits a transaction_ | ✅ | -| [eth_sendRawTransaction](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendrawtransaction) | _Submits a raw transaction_ | ✅ | -| [eth_getTransactionByHash](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash) | _Returns the information about a transaction requested by transaction hash_ | ✅ | -| [eth_getTransactionByBlockHashAndIndex](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyblockhashandindex) | _Returns information about a transaction by block hash and transaction index position_ | ✅ | -| [eth_getTransactionByBlockNumberAndIndex](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyblocknumberandindex) | _Returns information about a transaction by block number and transaction index position_ | ✅ | -| [eth_getTransactionReceipt](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionrecepit) | _Returns the receipt of a transaction by transaction hash_ | ✅ | - -## JSON-RPC methods according to the [Web3 Module API](https://openethereum.github.io/JSONRPC-web3-module) - -| Method | Description | Status | -| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------ | -| [web3_clientVersion](https://openethereum.github.io/JSONRPC-web3-module#web3_clientversion) | _Returns the current client version_ | ✅ | -| [web3_sha](https://openethereum.github.io/JSONRPC-web3-module#web3_sha3) | _Returns Keccak-256 (not the standardized SHA3-256) of the given data_ | ✅ | - -## JSON-RPC methods according to the [Net Module API](https://openethereum.github.io/JSONRPC-net-module) - -| Method | Description | Status | -| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------ | -| [net_listening](https://openethereum.github.io/JSONRPC-net-module#net_listening) | _Returns true if client is actively listening for network connections_ | ✅ | -| [net_peerCount](https://openethereum.github.io/JSONRPC-net-module#net_peercount) | _Returns number of peers currently connected to the client_ | ✅ | -| [net_version](https://openethereum.github.io/JSONRPC-net-module#net_version) | _Returns the current network protocol version_ | ✅ | - -You can find the complete set of available specs in the [Ethereum API Documentation](https://ethereum.github.io/execution-apis/api-documentation/). diff --git a/docs/content/references/iota-evm/magic-contract/introduction.md b/docs/content/references/iota-evm/magic-contract/introduction.md deleted file mode 100644 index 05ad5e71d5c..00000000000 --- a/docs/content/references/iota-evm/magic-contract/introduction.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -sidebar_position: 1 ---- - -import DocCardList from '@theme/DocCardList'; - -# Introduction - -This section documents the magic contract and all it's interfaces: - - - - - -## Call a Native Contract - -You can call native contracts using [`ISC.sandbox.call`](https://github.com/iotaledger/wasp/blob/develop/packages/vm/core/evm/iscmagic/ISCSandbox.sol#L56): - -```solidity -pragma solidity >=0.8.5; - -import "@iota/iscmagic/ISC.sol"; - -contract MyEVMContract { - event EntropyEvent(bytes32 entropy); - - function callInccounter() public { - ISCDict memory params = ISCDict(new ISCDictItem[](1)); - bytes memory int64Encoded42 = hex"2A00000000000000"; - params.items[0] = ISCDictItem("counter", int64Encoded42); - ISCAssets memory allowance; - ISC.sandbox.call(ISC.util.hn("inccounter"), ISC.util.hn("incCounter"), params, allowance); - } -} -``` - -`ISC.util.hn` is used to get the `hname` of the `inccounter` contract and the -`incCounter` entry point. You can also call view entry points using -[ISC.sandbox.callView](https://github.com/iotaledger/wasp/blob/develop/packages/vm/core/evm/iscmagic/ISCSandbox.sol#L59). diff --git a/docs/content/references/iota-evm/wasm-lib-data-types.mdx b/docs/content/references/iota-evm/wasm-lib-data-types.mdx deleted file mode 100644 index e7f3bbe790e..00000000000 --- a/docs/content/references/iota-evm/wasm-lib-data-types.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -tags: - - reference -description: The WasmLib provides direct support for the basic value data types that are found in all programming languages, and WasmLib versions of ISC-specific value data types. -image: /img/logo/WASP_logo_dark.png ---- - -# WasmLib Data Types - -You will need to manipulate data with your smart contracts. We distinguish two groups of -predefined data types that can be used in schema definition files. The WasmLib -implementations for each supported programming language provide full support for these -predefined data types. Each predefined data type can be (de)serialized as byte string or -as human-readable text string. - -## Basic Value Data Types - -These are mostly simple built-in scalar data types as provided by most programming -languages. Each integer data type has a clearly defined storage size. The -[Schema Tool](../../developer/iota-evm/schema/how-tos/usage.mdx) will attempt to use the closest matching built-in data type when -generating code for a specific language. - -- `BigInt` - An arbitrary-length unsigned integer. -- `Bool` - An 8-bit boolean value (0 or 1). -- `Bytes` - An arbitrary-length byte array. -- `Int8` - 8-bit signed integer value. -- `Int16` - 16-bit signed integer value. -- `Int32` - 32-bit signed integer value. -- `Int64` - 64-bit signed integer value. -- `String` - An arbitrary-length UTF-8 encoded string value. -- `Uint8` - 8-bit unsigned integer value. -- `Uint16` - 16-bit unsigned integer value. -- `Uint32` - 32-bit unsigned integer value. -- `Uint64` - 64-bit unsigned integer value. - -## ISC-specific Value Data Types - -These are ISC-specific value data types that are needed in the ISC sandbox function calls. -WasmLib provides its own implementations for each of the ISC value data types. - -- `Address` - A 33-byte encoded _Tangle_ address. -- `AgentID` - An ISC Agent ID (Address + Hname). -- `ChainID` - A 32-byte ISC Chain ID. -- `Hash` - A 32-byte hash value. -- `Hname` - A 4-byte unsigned integer hash value derived from a name string. -- `NftID` - A 32-byte ISC NFT ID. -- `RequestID` - A 34-byte ISC transaction request ID. -- `TokenID` - A 38-byte ISC token ID. - -## Full Matrix of WasmLib Types - -WasmLib implements a full set of [value proxies](../../developer/iota-evm/schema/proxies.mdx#value-proxies) for each -predefined value type that provide access to data on the ISC host. But there is one aspect -of this data that we have not considered yet. Some data provided by the host is mutable, -whereas other data may be immutable. To facilitate this distinction, each value proxy type -comes in two flavors that reflect this, and make sure that the data can only be used as -intended. - -The rule is that from an immutable container you can only derive immutable container and -value proxies. The referenced data can never be changed through immutable proxies. -Separating these constraints for types into separate value proxy types allows the use of -compile-time type-checking to enforce these constraints. To guard against client code that -tries to bypass them, the ISC sandbox will also check these constraints at runtime on the -host. - -| ISC type | WasmLib type | Mutable proxy | Immutable proxy | -|-----------|-------------------|------------------------|--------------------------| -| BigInt | Sc**BigInt** | ScMutable**BigInt** | ScImmutable**BigInt** | -| Bool | _boolean_ | ScMutable**Bool** | ScImmutable**Bool** | -| Bytes | _byte array_ | ScMutable**Bytes** | ScImmutable**Bytes** | -| Int8 | _8-bit signed_ | ScMutable**Int8** | ScImmutable**Int8** | -| Int16 | _16-bit signed_ | ScMutable**Int16** | ScImmutable**Int16** | -| Int32 | _32-bit signed_ | ScMutable**Int32** | ScImmutable**Int32** | -| Int64 | _64-bit signed_ | ScMutable**Int64** | ScImmutable**Int64** | -| String | _UTF-8 string_ | ScMutable**String** | ScImmutable**String** | -| Uint8 | _8-bit unsigned_ | ScMutable**Uint8** | ScImmutable**Uint8** | -| Uint16 | _16-bit unsigned_ | ScMutable**Uint16** | ScImmutable**Uint16** | -| Uint32 | _32-bit unsigned_ | ScMutable**Uint32** | ScImmutable**Uint32** | -| Uint64 | _64-bit unsigned_ | ScMutable**Uint64** | ScImmutable**Uint64** | -| | | | | -| Address | Sc**Address** | ScMutable**Address** | ScImmutable**Address** | -| AgentId | Sc**AgentId** | ScMutable**AgentId** | ScImmutable**AgentId** | -| ChainId | Sc**ChainId** | ScMutable**ChainId** | ScImmutable**ChainId** | -| Hash | Sc**Hash** | ScMutable**Hash** | ScImmutable**Hash** | -| Hname | Sc**Hname** | ScMutable**Hname** | ScImmutable**Hname** | -| NftID | Sc**NftID** | ScMutable**NftID** | ScImmutable**NftID** | -| RequestId | Sc**RequestId** | ScMutable**RequestId** | ScImmutable**RequestId** | -| TokenID | Sc**TokenID** | ScMutable**TokenID** | ScImmutable**TokenID** | - -The consistent naming makes it easy to remember the type names. Bool, Bytes, String, and -the integer types are the odd ones out. They are implemented in WasmLib by the closest -equivalents in the chosen WasmLib implementation programming language. - -The [Schema Tool](../../developer/iota-evm/schema/how-tos/usage.mdx) will automatically generate the proper immutable proxies -from the schema definition. For example, View functions will only be able to access the -[State](../../developer/iota-evm/schema/how-tos/state.mdx) map through immutable proxies. The same goes for the -[Params](../../developer/iota-evm/schema/how-tos/params.mdx) map that was passed into a Func or View, and for the -[Results](../../developer/iota-evm/schema/how-tos/results.mdx) map that was returned from a call to a Func or View. diff --git a/docs/content/references/_iota-graphql.mdx b/docs/content/references/iota-graphql.mdx similarity index 75% rename from docs/content/references/_iota-graphql.mdx rename to docs/content/references/iota-graphql.mdx index 6c60bcff812..1e1ca11bf4c 100644 --- a/docs/content/references/_iota-graphql.mdx +++ b/docs/content/references/iota-graphql.mdx @@ -5,7 +5,7 @@ description: GraphQL is a public service for the IOTA RPC that enables you to ef GraphQL for the IOTA RPC is a public service that enables interacting with the IOTA [network](https://iota.io/networkinfo). -To get started with GraphQL for the IOTA RPC, check out the [Getting Started](../developer/getting-started/_graphql-rpc.mdx) guide. If you'd like to learn more about the concepts used in the GraphQL service, check out the [GraphQL](../developer/_graphql-rpc.mdx) for IOTA RPC concepts page. +To get started with GraphQL for the IOTA RPC, check out the [Getting Started](../developer/getting-started/graphql-rpc.mdx) guide. If you'd like to learn more about the concepts used in the GraphQL service, check out the [GraphQL](../developer/graphql-rpc.mdx) for IOTA RPC concepts page. ## Key Types @@ -19,6 +19,6 @@ All GraphQL API elements are accessible via the left sidebar, the following are ## Related links -- [GraphQL migration](../developer/advanced/_graphql-migration.mdx): Migrating to GraphQL guides you through migrating IOTA RPC projects from JSON-RPC to GraphQL. -- [GraphQL quick-start](../developer/getting-started/_graphql-rpc.mdx): Querying IOTA RPC with GraphQL gets you started using GraphQL to query the IOTA RPC for on-chain data. -- [GraphQL concepts](../developer/_graphql-rpc.mdx): GraphQL for IOTA RPC examines the elements of GraphQL that you should know to get the most from the service. +- [GraphQL migration](../developer/advanced/graphql-migration.mdx): Migrating to GraphQL guides you through migrating IOTA RPC projects from JSON-RPC to GraphQL. +- [GraphQL quick-start](../developer/getting-started/graphql-rpc.mdx): Querying IOTA RPC with GraphQL gets you started using GraphQL to query the IOTA RPC for on-chain data. +- [GraphQL concepts](../developer/graphql-rpc.mdx): GraphQL for IOTA RPC examines the elements of GraphQL that you should know to get the most from the service. diff --git a/docs/content/references/iota-identity/.gitignore b/docs/content/references/iota-identity/.gitignore deleted file mode 100644 index f65e5817dd1..00000000000 --- a/docs/content/references/iota-identity/.gitignore +++ /dev/null @@ -1 +0,0 @@ -wasm diff --git a/docs/content/references/iota-identity/iota-did-method-spec.mdx b/docs/content/references/iota-identity/iota-did-method-spec.mdx deleted file mode 100644 index ca7b413b68a..00000000000 --- a/docs/content/references/iota-identity/iota-did-method-spec.mdx +++ /dev/null @@ -1,260 +0,0 @@ ---- -title: IOTA DID Method Specification v1.0 -sidebar_label: DID Method -description: How IOTA Identity implements the Decentralized Identifiers Standard on the IOTA Tangle. -image: /img/identity/icon.png -tags: - - reference - - identity - - did ---- - -# IOTA DID Method Specification v1.0 - -2023-08-23 - -## Abstract - -The IOTA DID Method Specification describes a method of implementing the [Decentralized Identifiers](https://www.w3.org/TR/did-core/) (DID) standard on [IOTA](https://iota.org), a Distributed Ledger Technology (DLT). It conforms to the [DID specification v1.0](https://www.w3.org/TR/did-core/) and describes how to perform Create, Read, Update and Delete (CRUD) operations for IOTA DID Documents using unspent transaction outputs (_UTXO_) on the IOTA and [Shimmer](https://shimmer.network/) networks, introduced with the [Stardust upgrade](https://blog.shimmer.network/stardust-upgrade-in-a-nutshell/). - -## Data Types & Subschema Notation - -Data types and subschemas used throughout this TIP are defined in [TIP-21](https://github.com/iotaledger/tips/blob/v1.2.0/tips/TIP-0021/tip-0021.md). - -## Introduction - -### UTXO Ledger - -The unspent transaction output ([UTXO](https://github.com/iotaledger/tips/blob/main/tips/TIP-0020/tip-0020.md)) model defines a ledger state which is comprised of unspent outputs. Outputs are created by a transaction consuming outputs of previous transactions as inputs. The Stardust version of the protocol defines several output types, the relevant ones for the IOTA DID Method are: Basic Outputs for _value transactions_, and Alias Outputs for storage of DID Documents. - -All outputs must hold a minimum amount of tokens to be stored on the ledger. For output types that can hold arbitrary data, for instance the Alias Output, the amount of tokens held by the output must cover the byte cost of the data stored. This prevents the ledger size from growing uncontrollably while guaranteeing that the data is not pruned from the nodes, which is important for resolving DID Documents. This deposit is fully refundable and can be reclaimed when the output is destroyed. - -Data stored in an output and covered by the storage deposit will be stored in _all_ nodes on the network and can be retrieved from any node. This provides strong guarantees for any data stored in the ledger. - -### Alias Output - -The [Alias Output](https://github.com/iotaledger/tips/blob/v1.2.0/tips/TIP-0018/tip-0018.md#alias-output) is a specific implementation of the [UTXO state machine](https://github.com/iotaledger/tips/blob/v1.2.0/tips/TIP-0018/tip-0018.md#chain-constraint-in-utxo). Some of its relevant properties are: - -- **Amount**: the amount of IOTA coins held by the output. -- **Alias ID**: 32 byte array, a unique identifier of the alias, which is the BLAKE2b-256 hash - of the Output ID that created it. -- **State Index**: A counter that must increase by 1 every time the alias is state transitioned. -- **State Metadata**: Dynamically sized array of arbitrary bytes with a length up to `Max Metadata Length`, as defined in [TIP-22](https://github.com/iotaledger/tips/blob/v1.2.0/tips/TIP-0022/tip-0022.md). Can only be changed by the state controller. -- **Unlock Conditions**: - - State Controller Address Unlock Condition - - Governor Address Unlock Condition - -Consuming an Alias Output in a transaction may transition it into the next state. The current state is defined as the consumed Alias Output, while the next state is defined as the **Alias Output with the same explicit `Alias ID` on the output side**. There are two types of transitions: `state transition` and `governance transition`. - -All outputs include an `Unlock Conditions` property. This feature defines how the output can be unlocked and spent. The Alias Output supports two types of unlock conditions that can be set: the state controller and governor. Each of these can be either an Ed25519 Address, Alias Address or an NFT Address. An Alias Output can have at most one of each unlock condition. - -The state controller can unlock a state transition. It is identified by an incremented `State Index` and can change the fields `Amount`, `State Index`, `State Metadata` among other properties. - -The governor, on the other hand, can unlock a governance transition indicated by an unchanged `State Index`. A governance transition can change the addresses of the state controller and governor. It also allows destroying the Alias Output. - -### Ledger and DID - -Storing DID Documents in the ledger state means they inherently benefit from the guarantees the ledger provides. - -1. Conflicts among nodes are resolved and dealt with by the ledger. -2. Replay attacks are mitigated since transactions need to be confirmed by the ledger. -3. Through the `State Index` a linear history for updates of a DID Document is provided. - -## DID Method Name - -The `method-name` to identify this DID method is: `iota`. - -A DID that uses this method MUST begin with the following prefix: `did:iota`. Following the generic DID specification, this string MUST be lowercase. - -## DID Format - -The DIDs that follow this method have the following ABNF syntax. It uses the syntax in [RFC5234](https://www.rfc-editor.org/rfc/rfc5234) and the corresponding definition for `digit`. - -``` -iota-did = "did:iota:" iota-specific-idstring -iota-specific-idstring = [ iota-network ":" ] iota-tag -iota-network = 1*6network-char -iota-tag = "0x" 64lowercase-hex -lowercase-hex = digit / "a" / "b" / "c" / "d" / "e" / "f" -network-char = %x61-7A / digit ; corresponds to the character range from "a" to "z" and "0" to "9". -``` - -It starts with the string "did:iota:", followed by an optional network name (1 to 6 lowercase alpha characters) and a colon, then the tag. -The tag starts with "0x" followed by a hex-encoded `Alias ID` with lower case a-f. - -### IOTA-Network - -The iota-network is an identifier of the network where the DID is stored. This network must be an IOTA Ledger, but can either be a public or private network, permissionless or permissioned. - -The following values are reserved and cannot reference other networks: - -1. `iota` references the main network which refers to the ledger known to host the IOTA cryptocurrency. -2. `atoi` references the development network of IOTA. -3. `smr` references the shimmer network. -4. `rms` references the development network of Shimmer. - -When no IOTA network is specified, it is assumed that the DID is located on the `iota` network. This means that the following DIDs will resolve to the same DID Document: - -``` -did:iota:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe -did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe -``` - -### IOTA-Tag - -An IOTA-tag is a hex-encoded `Alias ID`. The `Alias ID` itself is a unique identifier of the alias, which is the BLAKE2b-256 hash of the Output ID that created it. -This tag identifies the Alias Output where the DID Document is stored, and it will not be known before the generation of the DID since it will be assigned when the Alias Output is created. - -### Anatomy of the State Metadata - -In the `State Metadata` of the Alias Output must be a byte packed payload with header fields as follows: - -| Name | Type | Description | -| ------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | -| Version | uint8 | Set value **1** to denote the version number of this method | -| Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | -| Payload | (uint16)ByteArray | A DID Document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | - -The types are defined in [TIP-21](https://github.com/iotaledger/tips/blob/main/tips/TIP-0021/tip-0021.md#data-types). - -#### Payload - -The payload must contain the following fields: - -- `meta`: contains metadata about the DID Document. For example, `created` to indicate the time of - creation, and `updated` to indicate the time of the last update to the document. It may also include other properties. -- `doc`: contains the DID Document. In the example below, the document only contains one verification method. The `id` and `controller` is specified by `did:0:0` which references the DID of the document itself, since the DID is unknown at the time of publishing. It also deduplicates the DID of the document to reduce the size of the state metadata, in turn reducing the required storage deposit. - -Example State Metadata Document: - -```json -{ - "doc": { - "id": "did:0:0", - "verificationMethod": [ - { - "id": "did:0:0#jkGOGVO3Te7ADpvlplr47eP9ucLt41zm", - "controller": "did:0:0", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "alg": "EdDSA", - "kid": "jkGOGVO3Te7ADpvlplr47eP9ucLt41zm", - "crv": "Ed25519", - "x": "D5w8vG6tKEnpBAia5J4vNgLID8k0BspHz-cVMBCC3RQ" - } - } - ], - "authentication": ["did:0:0#jkGOGVO3Te7ADpvlplr47eP9ucLt41zm"] - }, - "meta": { - "created": "2023-08-28T14:49:37Z", - "updated": "2023-08-28T14:50:27Z" - } -} -``` - -## Controllers - -A state controller can directly update the DID Document and the amount of tokens held by the Alias Output, but it cannot destroy the output. A governor, on the other hand, can indirectly update the DID Document by updating the state controller. The governor can also destroy the output by performing a governance transition without producing an Alias Output with the same `Alias ID`. - -As of now, only one state controller and one governor can be set for an Alias Output. Support for multiple controllers may be possible depending on future updates of the protocol. - -## CRUD Operations - -Create, Read, Update and Delete (CRUD) operations that change the DID Documents are done through state or governance transitions of the Alias Output. - -**These operations require fund transfer to cover byte cost. Transactions must be carefully done in order to avoid fund loss.** For example, the amount of funds in the inputs should equal these in the outputs. Additionally, private keys of controllers must be stored securely. - -### Create - -In order to create a simple self controlled DID two things are required: - -1. An Ed25519 Address for which the private key is available, or control over an Alias or NFT Output. -2. A Basic, Alias or NFT Output with enough coins to cover the byte cost. - -Creation steps: - -1. Create the content of the DID Document like verification methods, services, etc. -2. Create the payload and the headers as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). -3. Create a new Alias Output with the payload and the headers stored in its `State Metadata`. -4. Set the state controller and the governor unlock conditions to the addresses that should control state and governance transitions, respectively. -5. Set enough tokens in the output to cover the byte cost. -6. Publish a new transaction with an existing output that contains at least the storage deposit from step 6 as input, and the newly created Alias Output as output. - -Once the transaction is confirmed, the DID is published and can be formatted by using the `Alias ID` as the tag in [DID Format](#did-format). - -### Read - -The following steps can be used to read the latest DID Document associated with a DID. - -1. Obtain the `Alias ID` from the DID by extracting the `iota-tag` from the DID, see [DID Format](#did-format). -2. Obtain the network of the DID by extracting the `iota-network` from the DID, see [DID Format](#did-format). -3. Query the Alias Output corresponding to the `Alias ID` using a node running the [inx indexer](https://github.com/iotaledger/inx-indexer). Nodes usually include this indexer by default. -4. Assert that the extracted network matches the one returned from the node. Return an error otherwise. -5. Assert that the `Alias ID` of the returned output matches the `Alias ID` extracted from the DID. Return an error otherwise. -6. Retrieve the value of the `State Metadata` field from the returned output. -7. Validate the contents match the structure described in [Anatomy of the State Metadata](#anatomy-of-the-state-metadata). Return an error otherwise. -8. Decode the DID Document from the `State Metadata`. -9. Replace the placeholder `did:0:0` with the DID given as input. - -### Update - -Updating a DID Document can be achieved by the state controller performing a state transition of the Alias Output with the updated content: - -1. Create a copy of the Alias Output with the `Alias ID` set explicitly. -2. Pack the updated DID Document, as described in the [Anatomy of the State Metadata](#anatomy-of-the-state-metadata), into the `State Metadata` of the output. -3. Increment the `State Index`. -4. Set the `amount` of coins sufficient to cover the byte cost. -5. Publish a new transaction that includes the current Alias Output as input (along with any required Basic Outputs to consume to cover the `amount`, if increased) and the updated one as output. If the state controller unlock of the Alias Output references other Alias or NFT Outputs, those outputs must be unlocked in the same transaction, recursively. - -### Delete - -#### Deactivate - -Temporarily deactivating a DID can be done by deleting the contents of the `State Meadata` in the Alias Output, setting it to an empty byte array, and publishing an [update](#update). - -Another option is to [update](#update) the DID Document and set the `deactivated` property in its `metadata` to true. In both cases, the deactivated DID Document will be marked as `deactivated` when resolved. - -#### Destroy - -In order to permanently destroy a DID, a new transaction can be published by the governor that consumes the Alias Output without having a corresponding Alias Output on the output side with the same explicit `Alias ID`. This results in destroying the Alias Output and the DID. - -Note that this operation irreversibly and irrecoverably deletes the DID. This is because the `Alias ID` from which an IOTA DID is derived (see [IOTA-Tag](#iota-tag)) is generated from the hash of the input transaction that created it, which cannot generally be replicated. - -## IOTA Identity standards - -The `did:iota` method is implemented in the [IOTA Identity framework](https://github.com/iotaledger/identity.rs). This framework supports a number of operations that are standardized, some are standardized across the SSI community, and some are the invention of the IOTA Foundation. - -### Revocation - -Revocation of verifiable credentials and signatures can be achieved using the [Revocation Bitmap 2022](./revocation-bitmap-2022.mdx) where issuers store a bitmap of indices in the DID Document. These indices correspond to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. - -### Standardized Services - -The IOTA Identity framework also standardized certain `services` that are embedded in the DID Document. It is RECOMMENDED to implement these when implementing the `did:iota` method. - -Currently standardized `services`: - -- [Revocation Bitmap Service](./revocation-bitmap-2022.mdx#revocation-bitmap-service) - -## Security Considerations - -The `did:iota` method is implemented on the [IOTA](https://iota.org), a public permissionless and feeless Distributed Ledger Technology (DLT), making it resistant against almost all censorship attack vectors. Up until the `Coordicide` update for the IOTA network, a dependency on the coordinator exists for resolving ordering conflicts. This has a minor censorship possibility, that, in the worst case, can prevent transactions from getting confirmed. - -### Private Key Management - -All private keys or seeds used for the `did:iota` method should be equally well protected by the users. Private keys of the state controller and the governor are especially important as they control how keys are added or removed, providing full control over the identity. The IOTA Identity framework utilizes the [Stronghold project](https://github.com/iotaledger/stronghold.rs), a secure software implementation isolating digital secrets from exposure to hacks or leaks. Developers may choose to add other ways to manage the private keys in a different manner. - -## Privacy Considerations - -### Personal Identifiable Information - -The public IOTA and Shimmer networks are immutable. This means that once something is included, it can never be completely removed. For example, destroying an Alias Output will remove it from the ledger state, but it can still be stored in permanodes or by any party that records historical ledger states. - -That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII, including inside DID Documents. While verifiable credentials can be made public, this should only be utilized by Identity for Organisations and Identity for Things. - -### Correlation Risks - -As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. See [DID Correlation Risks](https://www.w3.org/TR/did-core/#did-correlation-risks). Additionally, a DID can be correlated with funds if the Alias Output used to store the DID Document or any of its controllers is used for holding, transferring or controlling coins or NFTs. diff --git a/docs/content/references/iota-identity/overview.mdx b/docs/content/references/iota-identity/overview.mdx deleted file mode 100644 index 562ea861887..00000000000 --- a/docs/content/references/iota-identity/overview.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Specifications Overview -sidebar_label: Overview -description: Provide overview of the specifications -image: /img/identity/icon.png -tags: - - reference - - identity ---- - -While IOTA Identity implements many existing standards, it also adds some additional features we would like to standardize ourselves. This section covers these features and how they work in great detail. These are not light reads and can be skipped. - -The current specifications are: - -- [IOTA DID](iota-did-method-spec.mdx): The specification for the IOTA DID Method implemented on the _Tangle_. -- [Revocation Bitmap 2022](revocation-bitmap-2022.mdx): The specification for an on-Tangle credential revocation mechanism. diff --git a/docs/content/references/iota-identity/revocation-bitmap-2022.mdx b/docs/content/references/iota-identity/revocation-bitmap-2022.mdx deleted file mode 100644 index 28274808a5d..00000000000 --- a/docs/content/references/iota-identity/revocation-bitmap-2022.mdx +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: Revocation Bitmap -sidebar_label: Revocation Bitmap -description: The specification for the embedded revocation bitmap. -image: /img/identity/icon.png -tags: - - reference - - identity - - did ---- - -# Revocation Bitmap 2022 - -## Abstract - -This specification describes a mechanism for publishing the revocation status of [verifiable credentials](../../developer/iota-identity/explanations/verifiable-credentials.mdx) embedded in an issuer's DID document. - -## Introduction - -Revocation gives an issuer the capability to invalidate a credential they issued before its natural expiration date. To achieve this, issuers can embed an identifier in the `credentialStatus` field of a credential. Verifiers can then lookup that identifier in a separate list, to check whether the credential is still valid. This document specifies a mechanism of embedding such a list, in form of a bitmap, in the DID document of the issuer, where each bitmap index corresponds to a credential they have issued. This mechanism is space-efficient and enables a verifier to check a credential's status in a privacy-preserving manner and without requiring additional lookups or external resources. - -## Revocation Bitmap Concept - -The revocation status of a verifiable credential is expressed as a binary value. The issuer keeps a bitmap of indices corresponding to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. - -## Data Model - -### Revocation Bitmap Status - -For an issuer to enable verifiers to check the status of a verifiable credential, the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) property MUST be specified with the following properties: - -| Property | Description | -| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | The constraints on the `id` property are listed in the [Verifiable Credentials Data Model specification](https://www.w3.org/TR/vc-data-model/). The `id` MUST be a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) that is the URL to a [Revocation Bitmap Service](#revocation-bitmap-service) in the DID Document of the issuer. It SHOULD include an `index` query set to the same value as `revocationBitmapIndex`, to uniquely identify the `credentialStatus`. If the `index` query is present, implementations SHOULD reject statuses where the `index` query's value does not match `revocationBitmapIndex`. | -| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | -| `revocationBitmapIndex` | The `revocationBitmapIndex` property MUST be an unsigned, 32-bit integer expressed as a string. This is the index of the credential in the issuer's revocation bitmap. Each index SHOULD be unique among all credentials linking to the same [Revocation Bitmap Service](#revocation-bitmap-service). | - -#### Example - -An example of a verifiable credential with a `credentialStatus` of type `RevocationBitmap2022`. - -```json -{ - "@context": "https://www.w3.org/2018/credentials/v1", - "id": "https://example.edu/credentials/3732", - "type": ["VerifiableCredential", "UniversityDegreeCredential"], - "credentialSubject": { - "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", - "GPA": "4.0", - "degree": "Bachelor of Science and Arts", - "name": "Alice" - }, - "issuer": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "issuanceDate": "2022-06-13T08:04:36Z", - "credentialStatus": { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw?index=5#revocation", - "type": "RevocationBitmap2022", - "revocationBitmapIndex": "5" - }, - "proof": { - "type": "JcsEd25519Signature2020", - "verificationMethod": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", - "signatureValue": "2eHdbDumMrer4pNVkaiYMqsVqVp2adq7bRcgTJZiw17Zeghk2ZT49YHwLwCCg35YKganBhxP6YSbzYoBK1AuCUv" - } -} -``` - -### Revocation Bitmap Service - -To allow verifiers to check the status of a [Revocation Bitmap Status](#revocation-bitmap-status), the DID document of the credential issuer MUST contain a [service](https://www.w3.org/TR/did-core/#services) with the following properties: - -| Property | Description | -| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | The constraints on the `id` property are listed in the [DID Core service specification](https://www.w3.org/TR/did-core/#services). The `id` property MUST be a DID URL uniquely identifying the revocation bitmap. | -| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | -| `serviceEndpoint` | The `serviceEndpoint` MUST be generated according to the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). | - -#### Example - -An example of an issuer's DID document where credential `"5"` in the `#revocation` service is revoked: - -```json -{ - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "verificationMethod": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", - "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "z3hgM9fNkhwgT5mECbj1HdKoFNZgpffwQYEV8WBVHphXq" - } - ], - "capabilityInvocation": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#sign-0", - "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "z83F6zbD3KqaxvQhqo25LvSXzoDdpZmp3EpPVonSVACwZ" - } - ], - "service": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", - "type": "RevocationBitmap2022", - "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" - } - ] -} -``` - -## Algorithms - -The following algorithms define how to generate, expand and validate revocation bitmaps. - -### Service Endpoint Generation Algorithm - -The following process MUST be followed when producing a `RevocationBitmap2022` to embed in a service endpoint: - -1. Let **bitmap** be a [_roaring bitmap_](https://roaringbitmap.org/) where each bit is initialized to 0. -2. For each revoked credential with an **index** not exceeding an unsigned, 32-bit integer, set the corresponding bit in **bitmap** at **index** to 1. -3. Generate the **bitmap serialization** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/) using the **bitmap** as input. -4. Generate a **compressed bitmap** by using the ZLIB compression algorithm [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] on the **bitmap serialization** and base64-encoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the result. -5. Create the **service endpoint** by embedding the **compressed bitmap** in a data URL [[RFC2397](https://datatracker.ietf.org/doc/html/rfc2397)]. On the data url, the `` MUST be `application/octet-stream` and the `base64` attribute MUST be set. -6. Return the **service endpoint**. - -### Service Endpoint Expansion Algorithm - -The following process MUST be followed when expanding the endpoint from a service of type `RevocationBitmap2022`: - -1. Let **service endpoint** be a data url generated using the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). -2. The `` of the **service endpoint** MUST be `application/octet-stream` and the `base64` attribute MUST be set, return an error otherwise. Let **compressed bitmap** be the `` part of the data url. -3. Generate an **uncompressed bitmap** by base64-decoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the **compressed bitmap** and then decompressing the result using ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)]. -4. Generate the **bitmap** by deserializing the **uncompressed bitmap** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/). -5. Return the **bitmap**. - -### Validation Algorithm - -The following steps MUST be followed when checking whether a verifiable credential is revoked: - -1. Let **credential** be a verifiable credential containing a `credentialStatus` whose `type` is `RevocationBitmap2022`. -2. Let **revocation bitmap URL** be the `id` field of the **credential**'s `credentialStatus`. -3. Resolve the **revocation bitmap URL** to a **revocation bitmap service** in the issuer's DID document, and verify that the service `type` is `RevocationBitmap2022`. Return an error otherwise. -4. Expand the endpoint of the **revocation bitmap service** into a **revocation bitmap** according to the [service endpoint expansion algorithm](#service-endpoint-expansion-algorithm). -5. Let **revocation index** be the integer value of the `revocationBitmapIndex` property contained in the `credentialStatus` of the **credential**. -6. Let **revoked** be the value of the bit at index **revocation index** in the **revocation bitmap**. -7. Return `true` if **revoked** is 1, `false` otherwise. - -## Test Vectors - -This section provides test vectors to validate implementations against. - -### Test Vector 1 - -The following data URL decodes to a bitmap of length 0 where no index is revoked: - -`"data:application/octet-stream;base64,ZUp5ek1tQUFBd0FES0FCcg=="` - -### Test Vector 2 - -The following data URL decodes to a bitmap of length 3 where indices `5`, `398`, and `67000` are revoked: - -`"data:application/octet-stream;base64,ZUp5ek1tQmdZR0lBQVVZZ1pHQ1FBR0laSUdabDZHUGN3UW9BRXVvQjlB"`. - -### Test Vector 3 - -The following data URL decodes to a bitmap of length 16384 where all indices are revoked: - -`"data:application/octet-stream;base64,ZUp6dHhERVJBQ0FNQkxESEFWS1lXZkN2Q3E0MmFESmtyMlNrM0ROckFLQ2RBQUFBQUFBQTMzbGhHZm9q"` - -## Rationale - -This section describes the rationale behind some of the design decisions of this specification. - -### Compression and maximum size - -Considering that messages published to the Tangle cannot exceed [32 KiB](https://github.com/iotaledger/tips/blob/v1.2.0/tips/TIP-0006/tip-0006.md#message-validation) in size, and that larger messages have increased requirements, the use of compression was assessed. -The precise size of a serialized bitmap varies based on the number and distribution of revoked indices. When indices are revoked uniformly randomly, roughly 100,000 - 200,000 can be achieved in a DID Document with compression, and significantly more if consecutive indices are revoked. - -ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] was chosen for having a free and open source software licence and being one of the most widely used compression schemes, which enhances the accessibility of this specification. Some other assessed algorithms produced only marginally better compression ratios but had far fewer existing implementations across different programming languages. - -### Compressed Bitstring vs. Roaring Bitmap - -Because of its space efficiency, a roaring bitmap is preferred for representing a bitmap in-memory. To avoid the dependency on roaring bitmap, we considered using a compressed bitstring as the serialization format. However, serialization of such a bitstring was 2-3x slower compared to roaring's serialization format, which becomes an issue on resource-constrained devices (e.g. smartphones) or in web browsers. - -### Comparison to `RevocationList2020` and `StatusList2021` - -The [RevocationList2020 specification](https://w3c-ccg.github.io/vc-status-rl-2020/) and [StatusList2021 specification](https://w3c-ccg.github.io/vc-status-list-2021/) both describe a similar revocation mechanism using a verifiable credential that contains a bitmap, similar to the `RevocationBitmap2022` approach. The credential is hosted outside of the DID document and the verifier thus needs to fetch it from an external resource, likely one controlled by the issuer. This has privacy implications as the issuer can track where a fetch request for the credential came from and potentially infer who the credential was verified by and for what purpose. The issuer can also potentially infer which credential was checked. Because `RevocationBitmap2022` is embedded in the issuer's DID document, which can be obtained without the their knowledge, this approach does not suffer from these privacy shortcomings. See also the [privacy considerations](#privacy-considerations). - -A downside of embedding the revocation list in the DID document is that storage in a distributed ledger (DLT) is usually more expensive than other storage hosting solutions. The DLT might also impose message size limitations, capping the total number of revocations that can be done (see also [compression](#compression-and-maximum-size)). - -Another difference is that `RevocationList2020` specifies a minimum initial size of 131,072 for its bitstring, to mitigate the potential for correlating individuals when few credentials have been issued. `RevocationBitmap2022` uses a roaring bitmap instead of a bitstring, so the maximum size is not fixed (apart from the upper bound of an unsigned 32-bit integer). This means the bitmap cannot be used to correlate small populations without more information not present in the bitmap itself. However, both schemes still reveal publicly how many credentials have been revoked, which could be used to infer other information if more knowledge about how an issuer assigns credential revocation indexes is known. - -`StatusList2021` allows for explicitly stating the purpose of the list, currently either _revocation_ or _suspension_. This specification does not mandate that revoked credentials cannot be unrevoked, which means a `RevocationBitmap2022` can effectively also be used as a suspension list. - -### Privacy Considerations - -Because the revocation bitmap is embedded in the DID document, and thus available without contacting the issuer directly, the issuer cannot correlate how a holder uses their credential. - -An observer finding a service of type `RevocationBitmap2022` in a DID document can infer that this DID belongs to an issuer. However, DIDs of issuers tend to be publicly known, in contrast to DIDs of other entities, so this is unlikely to present an issue. External observers can monitor the frequency of revocations and potentially the total number of issued credentials, depending on how the issuer assigns credential indices (e.g. starting from 0 and incrementing the index for each issued credential). diff --git a/docs/content/references/iota-identity/revocation-timeframe-2024.mdx b/docs/content/references/iota-identity/revocation-timeframe-2024.mdx deleted file mode 100644 index d6cba37b47a..00000000000 --- a/docs/content/references/iota-identity/revocation-timeframe-2024.mdx +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: Revocation Timeframe 2024 -sidebar_label: Revocation Timeframe 2024 -description: The specification for an embeddable time-based revocation method - `RevocationTimeframe2024`. -image: /img/identity/icon.png -tags: - - reference - - identity - - did ---- - -# Revocation Timeframe 2024 - -## Abstract - -This specification describes a new revocation mechanism - `RevocationTimeframe2024` - that extends [`RevocationBitmap2022`](revocation-bitmap-2022.mdx) -to address its linkability security concerns, at least when used in combination with selectively disclosable (SD-able) VCs. - -## Introduction - -`RevocationBitmap2022` allows for a simple and straightforward way for an issuer to invalidate an issued VC before its expiration. While this method prevents the analysis of usage by the issuer through hosting the revocation information on-chain -it comes with a high risk of linkability by colluding verifiers as they can store the bitmap index and use it to identify users. Additionally, verifiers can monitor the revocation information and at a later point, outside of any interaction, check the revocation status. -To address this privacy concern, `RevocationTimeframe2024` was designed as an extension that builds on top of `RevocationBitmap2022`, therefore sharing -most of its logic. - -## Concepts -`RevocationTimeframe2024` should be used together with selectively disclosable credentials - either [SD-JWT](../../developer/iota-identity/how-tos/verifiable-credentials/selective-disclosure) -or [ZK-SD](../../developer/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure) - in order to conceal to the verifier -the credential's index in the issuer's revocation bitmap to avoid linkability. - -### Validity Timeframe - -If the revocation index is concealed from the verifier how can it assert the validity of the presented credential? -To solve this issue `RevocationTimeframe2024` introduces the concept of a _validity timeframe_, i.e. a limited time span -in which the credential is guaranteed to be non-revoked. By having a validity timeframe embedded in the credential's status -the verifier can prove the credential's validity by verifying the credential's signature. - -The downside of this mechanism is that the credential holder using `RevocationTimeframe2024` has to contact the credential's issuer -to update the signature - i.e. re-issue the credential - at the end of the credentials validity timeframe. Furthermore, -given how a credentials validity timeframe proves the validity of the credential itself, the updates made by the issuer to the credential's status - -i.e., revoking or un-revoking it - won't be reflected on the credential until a new validity timeframe starts. For this reason, -issuers should choose a validity timeframe length with respect to how frequently they expect to change the credential's status; -frequent updates deem shorter validity timeframes. - -:::note -A credential holder does not need to have its credential re-issued at the end of every validity timeframe, but only when -they need to present the credential to a verifier and its validity timeframe has expired. -::: - -## Data Model - -### Revocation Timeframe Status - -For an issuer to enable verifiers to check the status of a verifiable credential, the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) property MUST be specified with the following properties: - -| Property | Description | -| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | The constraints on the `id` property are listed in the [Verifiable Credentials Data Model specification](https://www.w3.org/TR/vc-data-model/). The `id` MUST be a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) that is the URL to a [Revocation Timeframe Service](#revocation-timeframe-service) in the DID Document of the issuer. | -| `type` | The `type` property MUST be `"RevocationTimeframe2024"`. | -| `revocationBitmapIndex` | The `revocationBitmapIndex` property MUST be an unsigned, 32-bit integer expressed as a string. This is the index of the credential in the issuers revocation bitmap. Each index SHOULD be unique among all credentials linking to the same [Revocation Timeframe Service](#revocation-timeframe-service). To ensure user unlinkability, this value MUST NOT be disclosed to the verifier (this is done by default). | -| `startValidityTimeframe`| The `startValidityTimeframe` property MUST be a [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)-compliant timestamp. | -| `endValidityTimeframe`| The `endValidityTimeframe` property MUST be a [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)-compliant timestamp. | - -#### Example - -An example of a verifiable credential with a `credentialStatus` of type `RevocationTimeframe2024`. - -```json -{ - "@context": "https://www.w3.org/2018/credentials/v1", - "id": "https://example.edu/credentials/3732", - "type": ["VerifiableCredential", "UniversityDegreeCredential"], - "credentialSubject": { - "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", - "GPA": "4.0", - "degree": "Bachelor of Science and Arts", - "name": "Alice" - }, - "issuer": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "issuanceDate": "2022-06-13T08:04:36Z", - "credentialStatus": { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#my-revocation-service", - "type": "RevocationTimeframe2024", - "revocationBitmapIndex": "5" - "startValidityTimeframe": "2024-05-03T08:00:00Z", - "endValidityTimeframe": "2024-05-03T08:05:00Z", - }, -} -``` - -### Revocation Timeframe Service - -To allow verifiers to check the status of a [Revocation Timeframe Status](#revocation-timeframe-status), the DID document of the credential issuer MUST contain a [service](https://www.w3.org/TR/did-core/#services) with the following properties: - -| Property | Description | -| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | The constraints on the `id` property are listed in the [DID Core service specification](https://www.w3.org/TR/did-core/#services). The `id` property MUST be a DID URL uniquely identifying the revocation bitmap. | -| `type` | The `type` property MUST be `"RevocationTimeframe2024"`. | -| `serviceEndpoint` | The `serviceEndpoint` MUST be generated according to the service endpoint generation algorithm described in [`RevocationBitmap2022`](revocation-bitmap-2022.mdx#service-endpoint-generation-algorithm). | - -#### Example - -An example of an issuers DID document where credential `"5"` in the `#revocation` service is revoked: - -```json -{ - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "verificationMethod": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", - "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "z3hgM9fNkhwgT5mECbj1HdKoFNZgpffwQYEV8WBVHphXq" - } - ], - "capabilityInvocation": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#sign-0", - "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", - "type": "Ed25519VerificationKey2018", - "publicKeyMultibase": "z83F6zbD3KqaxvQhqo25LvSXzoDdpZmp3EpPVonSVACwZ" - } - ], - "service": [ - { - "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", - "type": "RevocationTimeframe2024", - "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" - } - ] -} -``` - -## Algorithms - -For generation and expansion of the service endpoint see [`RevocationBitmap2022`](revocation-bitmap-2022.mdx#algorithms). - -### Validation Algorithm - -The following steps MUST be followed when checking whether a verifiable credential is revoked: - -1. Let **credential** be a verifiable credential containing a `credentialStatus` whose `type` is `RevocationTimeframe2024`. -2. Let **now** be the string serialization of the RFC3339 timestamp representing the current UTC time. -3. Let **start validity timeframe** and **end validity timeframe** be the RFC3339 timestamps of the `startValidityTimeframe` and `endValidityTimeframe` (respectively) properties contained in the `credentialStatus` of the **credential**. -4. Return `true` if `startValidityTimeframe <= now < endValidityTimeframe`, `false` otherwise. - -## Test Vectors -See [`RevocationBitmap2022`](revocation-bitmap-2022.mdx#test-vectors). \ No newline at end of file diff --git a/docs/content/references/move/abilities.mdx b/docs/content/references/move/abilities.mdx new file mode 100644 index 00000000000..1f34d29be8c --- /dev/null +++ b/docs/content/references/move/abilities.mdx @@ -0,0 +1,240 @@ +# Abilities + +Abilities are a typing feature in Move that control what actions are permissible for values of a given type. This system grants fine grained control over the "linear" typing behavior of values, as well as if and how values are used in global storage. This is implemented by gating access to certain bytecode instructions so that for a value to be used with the bytecode instruction, it must have the ability required (if one is required at all—not every instruction is gated by an ability). + +## The Four Abilities + +The four abilities are: + +* [`copy`](#copy) + * Allows values of types with this ability to be copied. +* [`drop`](#drop) + * Allows values of types with this ability to be popped/dropped. +* [`store`](#store) + * Allows values of types with this ability to exist inside a struct in global storage. +* [`key`](#key) + * Allows the type to serve as a key for global storage operations. + +### `copy` + +The `copy` ability allows values of types with that ability to be copied. It gates the ability to copy values out of local variables with the `copy` operator and to copy values via references with dereference `*e`. + +If a value has `copy`, all values contained inside of that value have `copy`. + +### `drop` + +The `drop` ability allows values of types with that ability to be dropped. By dropped, we mean that value is not transferred and is effectively destroyed as the Move program executes. As such, this ability gates the ability to ignore values in a multitude of locations, including: +* not using the value in a local variable or parameter +* not using the value in a sequence via `;` +* overwriting values in variables in assignments +* overwriting values via references when writing `*e1 = e2`. + +If a value has `drop`, all values contained inside of that value have `drop`. + +### `store` + +The `store` ability allows values of types with this ability to exist inside of a struct (resource) in global storage, *but* not necessarily as a top-level resource in global storage. This is the only ability that does not directly gate an operation. Instead it gates the existence in global storage when used in tandem with `key`. + +If a value has `store`, all values contained inside of that value have `store` + +### `key` + +The `key` ability allows the type to serve as a key for global storage operations. It gates all global storage operations, so in order for a type to be used with `move_to`, `borrow_global`, `move_from`, etc., the type must have the `key` ability. Note that the operations still must be used in the module where the `key` type is defined (in a sense, the operations are private to the defining module). + +If a value has `key`, all values contained inside of that value have `store`. This is the only ability with this sort of asymmetry. + +## Builtin Types + +Most primitive, builtin types have `copy`, `drop`, and `store` with the exception of `signer`, which just has `drop` + +* `bool`, `u8`, `u16`, `u32`, `u64`, `u128`, `u256`, and `address` all have `copy`, `drop`, and `store`. +* `signer` has `drop` + * Cannot be copied and cannot be put into global storage +* `vector` may have `copy`, `drop`, and `store` depending on the abilities of `T`. + * See [Conditional Abilities and Generic Types](#conditional-abilities-and-generic-types) for more details. +* Immutable references `&` and mutable references `&mut` both have `copy` and `drop`. + * This refers to copying and dropping the reference itself, not what they refer to. + * References cannot appear in global storage, hence they do not have `store`. + +None of the primitive types have `key`, meaning none of them can be used directly with the global storage operations. + +## Annotating Structs + +To declare that a `struct` has an ability, it is declared with `has ` after the struct name but before the fields. For example: + +```move +struct Ignorable has drop { f: u64 } +struct Pair has copy, drop, store { x: u64, y: u64 } +``` + +In this case: `Ignorable` has the `drop` ability. `Pair` has `copy`, `drop`, and `store`. + + +All of these abilities have strong guarantees over these gated operations. The operation can be performed on the value only if it has that ability; even if the value is deeply nested inside of some other collection! + +As such: when declaring a struct’s abilities, certain requirements are placed on the fields. All fields must satisfy these constraints. These rules are necessary so that structs satisfy the reachability rules for the abilities given above. If a struct is declared with the ability... + +* `copy`, all fields must have `copy`. +* `drop`, all fields must have `drop`. +* `store`, all fields must have `store`. +* `key`, all fields must have `store`. + * `key` is the only ability currently that doesn’t require itself. + +For example: + +```move +// A struct without any abilities +struct NoAbilities {} + +struct WantsCopy has copy { + f: NoAbilities, // ERROR 'NoAbilities' does not have 'copy' +} +``` + +and similarly: + +```move +// A struct without any abilities +struct NoAbilities {} + +struct MyResource has key { + f: NoAbilities, // Error 'NoAbilities' does not have 'store' +} +``` + +## Conditional Abilities and Generic Types + +When abilities are annotated on a generic type, not all instances of that type are guaranteed to have that ability. Consider this struct declaration: + +``` +struct Cup has copy, drop, store, key { item: T } +``` + +It might be very helpful if `Cup` could hold any type, regardless of its abilities. The type system can *see* the type parameter, so it should be able to remove abilities from `Cup` if it *sees* a type parameter that would violate the guarantees for that ability. + +This behavior might sound a bit confusing at first, but it might be more understandable if we think about collection types. We could consider the builtin type `vector` to have the following type declaration: + +``` +vector has copy, drop, store; +``` + +We want `vector`s to work with any type. We don't want separate `vector` types for different abilities. So what are the rules we would want? Precisely the same that we would want with the field rules above. So, it would be safe to copy a `vector` value only if the inner elements can be copied. It would be safe to ignore a `vector` value only if the inner elements can be ignored/dropped. And, it would be safe to put a `vector` in global storage only if the inner elements can be in global storage. + +To have this extra expressiveness, a type might not have all the abilities it was declared with depending on the instantiation of that type; instead, the abilities a type will have depends on both its declaration **and** its type arguments. For any type, type parameters are pessimistically assumed to be used inside of the struct, so the abilities are only granted if the type parameters meet the requirements described above for fields. Taking `Cup` from above as an example: + +* `Cup` has the ability `copy` only if `T` has `copy`. +* It has `drop` only if `T` has `drop`. +* It has `store` only if `T` has `store`. +* It has `key` only if `T` has `store`. + +Here are examples for this conditional system for each ability: + +### Example: conditional `copy` + +```move +struct NoAbilities {} +struct S has copy, drop { f: bool } +struct Cup has copy, drop, store { item: T } + +fun example(c_x: Cup, c_s: Cup) { + // Valid, 'Cup' has 'copy' because 'u64' has 'copy' + let c_x2 = copy c_x; + // Valid, 'Cup' has 'copy' because 'S' has 'copy' + let c_s2 = copy c_s; +} + +fun invalid(c_account: Cup, c_n: Cup) { + // Invalid, 'Cup' does not have 'copy'. + // Even though 'Cup' was declared with copy, the instance does not have 'copy' + // because 'signer' does not have 'copy' + let c_account2 = copy c_account; + // Invalid, 'Cup' does not have 'copy' + // because 'NoAbilities' does not have 'copy' + let c_n2 = copy c_n; +} +``` + +### Example: conditional `drop` + +```move +struct NoAbilities {} +struct S has copy, drop { f: bool } +struct Cup has copy, drop, store { item: T } + +fun unused() { + Cup { item: true }; // Valid, 'Cup' has 'drop' + Cup { item: S { f: false }}; // Valid, 'Cup' has 'drop' +} + +fun left_in_local(c_account: Cup): u64 { + let c_b = Cup { item: true }; + let c_s = Cup { item: S { f: false }}; + // Valid return: 'c_account', 'c_b', and 'c_s' have values + // but 'Cup', 'Cup', and 'Cup' have 'drop' + 0 +} + +fun invalid_unused() { + // Invalid, Cannot ignore 'Cup' because it does not have 'drop'. + // Even though 'Cup' was declared with 'drop', the instance does not have 'drop' + // because 'NoAbilities' does not have 'drop' + Cup { item: NoAbilities {}}; +} + +fun invalid_left_in_local(): u64 { + let n = Cup { item: NoAbilities {}}; + // Invalid return: 'n' has a value + // and 'Cup' does not have 'drop' + 0 +} +``` + +### Example: conditional `store` + +```move +struct Cup has copy, drop, store { item: T } + +// 'MyInnerResource' is declared with 'store' so all fields need 'store' +struct MyInnerResource has store { + yes: Cup, // Valid, 'Cup' has 'store' + // no: Cup, Invalid, 'Cup' does not have 'store' +} + +// 'MyResource' is declared with 'key' so all fields need 'store' +struct MyResource has key { + yes: Cup, // Valid, 'Cup' has 'store' + inner: Cup, // Valid, 'Cup' has 'store' + // no: Cup, Invalid, 'Cup' does not have 'store' +} +``` + +### Example: conditional `key` + +```move +struct NoAbilities {} +struct MyResource has key { f: T } + +fun valid(account: &signer) acquires MyResource { + let addr = signer::address_of(account); + // Valid, 'MyResource' has 'key' + let has_resource = exists>(addr); + if (!has_resource) { + // Valid, 'MyResource' has 'key' + move_to(account, MyResource { f: 0 }) + }; + // Valid, 'MyResource' has 'key' + let r = borrow_global_mut>(addr) + r.f = r.f + 1; +} + +fun invalid(account: &signer) { + // Invalid, 'MyResource' does not have 'key' + let has_it = exists>(addr); + // Invalid, 'MyResource' does not have 'key' + let NoAbilities {} = move_from(addr); + // Invalid, 'MyResource' does not have 'key' + move_to(account, NoAbilities {}); + // Invalid, 'MyResource' does not have 'key' + borrow_global(addr); +} +``` \ No newline at end of file diff --git a/docs/content/references/move/generics.mdx b/docs/content/references/move/generics.mdx new file mode 100644 index 00000000000..d2ea5e7dd89 --- /dev/null +++ b/docs/content/references/move/generics.mdx @@ -0,0 +1,482 @@ +# Generics + +Generics can be used to define functions and structs over different input data types. This language feature is sometimes referred to as *parametric polymorphism*. In Move, we will often use the term generics interchangeably with type parameters and type arguments. + +Generics are commonly used in library code, such as in vector, to declare code that works over any possible instantiation (that satisfies the specified constraints). In other frameworks, generic code can sometimes be used to interact with global storage many different ways that all still share the same implementation. + +## Declaring Type Parameters + +Both functions and structs can take a list of type parameters in their signatures, enclosed by a pair of angle brackets `<...>`. + +### Generic Functions + +Type parameters for functions are placed after the function name and before the (value) parameter list. The following code defines a generic identity function that takes a value of any type and returns that value unchanged. + +```move +fun id(x: T): T { + // this type annotation is unnecessary but valid + (x: T) +} +``` + +Once defined, the type parameter `T` can be used in parameter types, return types, and inside the function body. + +### Generic Structs + +Type parameters for structs are placed after the struct name, and can be used to name the types of the fields. + +```move +struct Foo has copy, drop { x: T } + +struct Bar has copy, drop { + x: T1, + y: vector, +} +``` + +Note that [type parameters do not have to be used](#unused-type-parameters) + +## Type Arguments + +### Calling Generic Functions + +When calling a generic function, one can specify the type arguments for the function's type parameters in a list enclosed by a pair of angle brackets. + +```move +fun foo() { + let x = id(true); +} +``` + +If you do not specify the type arguments, Move's [type inference](#type-inference) will supply them for you. + +### Using Generic Structs + +Similarly, one can attach a list of type arguments for the struct's type parameters when constructing or destructing values of generic types. + +```move +fun foo() { + let foo = Foo { x: true }; + let Foo { x } = foo; +} +``` + +If you do not specify the type arguments, Move's [type inference](#type-inference) will supply them for you. + +### Type Argument Mismatch + +If you specify the type arguments and they conflict with the actual values supplied, an error will be given: + +```move +fun foo() { + let x = id(true); // error! true is not a u64 +} +``` + +and similarly: + +```move +fun foo() { + let foo = Foo { x: 0 }; // error! 0 is not a bool + let Foo
{ x } = foo; // error! bool is incompatible with address +} +``` + +## Type Inference + +In most cases, the Move compiler will be able to infer the type arguments so you don't have to write them down explicitly. Here's what the examples above would look like if we omit the type arguments: + +```move +fun foo() { + let x = id(true); + // ^ is inferred + + let foo = Foo { x: true }; + // ^ is inferred + + let Foo { x } = foo; + // ^ is inferred +} +``` + +Note: when the compiler is unable to infer the types, you'll need annotate them manually. A common scenario is to call a function with type parameters appearing only at return positions. + +```move +address 0x2 { +module m { + using std::vector; + + fun foo() { + // let v = vector::new(); + // ^ The compiler cannot figure out the element type. + + let v = vector::new(); + // ^~~~~ Must annotate manually. + } +} +} +``` + +However, the compiler will be able to infer the type if that return value is used later in that function: + +```move +address 0x2 { +module m { + using std::vector; + + fun foo() { + let v = vector::new(); + // ^ is inferred + vector::push_back(&mut v, 42); + } +} +} +``` + +## Unused Type Parameters + +For a struct definition, +an unused type parameter is one that +does not appear in any field defined in the struct, +but is checked statically at compile time. +Move allows unused type parameters so the following struct definition is valid: + +```move +struct Foo { + foo: u64 +} +``` + +This can be convenient when modeling certain concepts. Here is an example: + +```move +address 0x2 { +module m { + // Currency Specifiers + struct Currency1 {} + struct Currency2 {} + + // A generic coin type that can be instantiated using a currency + // specifier type. + // e.g. Coin, Coin etc. + struct Coin has store { + value: u64 + } + + // Write code generically about all currencies + public fun mint_generic(value: u64): Coin { + Coin { value } + } + + // Write code concretely about one currency + public fun mint_concrete(value: u64): Coin { + Coin { value } + } +} +} +``` + +In this example, +`struct Coin` is generic on the `Currency` type parameter, +which specifies the currency of the coin and +allows code to be written either +generically on any currency or +concretely on a specific currency. +This genericity applies even when the `Currency` type parameter +does not appear in any of the fields defined in `Coin`. + +### Phantom Type Parameters + +In the example above, +although `struct Coin` asks for the `store` ability, +neither `Coin` nor `Coin` will have the `store` ability. +This is because of the rules for +[Conditional Abilities and Generic Types](./abilities.mdx#conditional-abilities-and-generic-types) +and the fact that `Currency1` and `Currency2` don't have the `store` ability, +despite the fact that they are not even used in the body of `struct Coin`. +This might cause some unpleasant consequences. +For example, we are unable to put `Coin` into a wallet in the global storage. + +One possible solution would be to +add spurious ability annotations to `Currency1` and `Currency2` +(i.e., `struct Currency1 has store {}`). +But, this might lead to bugs or security vulnerabilities +because it weakens the types with unnecessary ability declarations. +For example, we would never expect a resource in the global storage to have a field in type `Currency1`, +but this would be possible with the spurious `store` ability. +Moreover, the spurious annotations would be infectious, +requiring many functions generic on the unused type parameter to also include the necessary constraints. + +Phantom type parameters solve this problem. +Unused type parameters can be marked as *phantom* type parameters, +which do not participate in the ability derivation for structs. +In this way, +arguments to phantom type parameters are not considered when deriving the abilities for generic types, +thus avoiding the need for spurious ability annotations. +For this relaxed rule to be sound, +Move's type system guarantees that a parameter declared as `phantom` is either +not used at all in the struct definition, or +it is only used as an argument to type parameters also declared as `phantom`. + +#### Declaration + +In a struct definition +a type parameter can be declared as phantom by adding the `phantom` keyword before its declaration. +If a type parameter is declared as phantom we say it is a phantom type parameter. +When defining a struct, Move's type checker ensures that every phantom type parameter is either +not used inside the struct definition or +it is only used as an argument to a phantom type parameter. + +More formally, +if a type is used as an argument to a phantom type parameter +we say the type appears in _phantom position_. +With this definition in place, +the rule for the correct use of phantom parameters can be specified as follows: +**A phantom type parameter can only appear in phantom position**. + +The following two examples show valid uses of phantom parameters. +In the first one, +the parameter `T1` is not used at all inside the struct definition. +In the second one, the parameter `T1` is only used as an argument to a phantom type parameter. + +```move +struct S1 { f: u64 } + ^^ + Ok: T1 does not appear inside the struct definition + + +struct S2 { f: S1 } + ^^ + Ok: T1 appears in phantom position +``` + +The following code shows examples of violations of the rule: + +```move +struct S1 { f: T } + ^ + Error: Not a phantom position + +struct S2 { f: T } + +struct S3 { f: S2 } + ^ + Error: Not a phantom position +``` + +#### Instantiation + +When instantiating a struct, +the arguments to phantom parameters are excluded when deriving the struct abilities. +For example, consider the following code: + +```move +struct S has copy { f: T1 } +struct NoCopy {} +struct HasCopy has copy {} +``` + +Consider now the type `S`. +Since `S` is defined with `copy` and all non-phantom arguments have `copy` +then `S` also has `copy`. + +#### Phantom Type Parameters with Ability Constraints + +Ability constraints and phantom type parameters are orthogonal features in the sense that +phantom parameters can be declared with ability constraints. +When instantiating a phantom type parameter with an ability constraint, +the type argument has to satisfy that constraint, +even though the parameter is phantom. +For example, the following definition is perfectly valid: + +```move +struct S {} +``` + +The usual restrictions apply and `T` can only be instantiated with arguments having `copy`. + +## Constraints + +In the examples above, we have demonstrated how one can use type parameters to define "unknown" types that can be plugged in by callers at a later time. This however means the type system has little information about the type and has to perform checks in a very conservative way. In some sense, the type system must assume the worst case scenario for an unconstrained generic. Simply put, by default generic type parameters have no [abilities](./abilities.mdx). + +This is where constraints come into play: they offer a way to specify what properties these unknown types have so the type system can allow operations that would otherwise be unsafe. + +### Declaring Constraints + +Constraints can be imposed on type parameters using the following syntax. + +```move +// T is the name of the type parameter +T: (+ )* +``` + +The `` can be any of the four [abilities](./abilities.mdx), and a type parameter can be constrained with multiple abilities at once. So all of the following would be valid type parameter declarations: + +```move +T: copy +T: copy + drop +T: copy + drop + store + key +``` + +### Verifying Constraints + +Constraints are checked at call sites so the following code won't compile. + +```move +struct Foo { x: T } + +struct Bar { x: Foo } +// ^ error! u8 does not have 'key' + +struct Baz { x: Foo } +// ^ error! T does not have 'key' +``` + +```move +struct R {} + +fun unsafe_consume(x: T) { + // error! x does not have 'drop' +} + +fun consume(x: T) { + // valid! + // x will be dropped automatically +} + +fun foo() { + let r = R {}; + consume(r); + // ^ error! R does not have 'drop' +} +``` + +```move +struct R {} + +fun unsafe_double(x: T) { + (copy x, x) + // error! x does not have 'copy' +} + +fun double(x: T) { + (copy x, x) // valid! +} + +fun foo(): (R, R) { + let r = R {}; + double(r) + // ^ error! R does not have 'copy' +} +``` + +For more information, see the abilities section on [conditional abilities and generic types](./abilities.mdx#conditional-abilities-and-generic-types). + +## Limitations on Recursions + +### Recursive Structs + +Generic structs can not contain fields of the same type, either directly or indirectly, even with different type arguments. All of the following struct definitions are invalid: + +```move +struct Foo { + x: Foo // error! 'Foo' containing 'Foo' +} + +struct Bar { + x: Bar // error! 'Bar' containing 'Bar' +} + +// error! 'A' and 'B' forming a cycle, which is not allowed either. +struct A { + x: B +} + +struct B { + x: A + y: A +} +``` + +### Advanced Topic: Type-level Recursions + +Move allows generic functions to be called recursively. However, when used in combination with generic structs, this could create an infinite number of types in certain cases, and allowing this means adding unnecessary complexity to the compiler, vm and other language components. Therefore, such recursions are forbidden. + +Allowed: + +```move +address 0x2 { +module m { + struct A {} + + // Finitely many types -- allowed. + // foo -> foo -> foo -> ... is valid + fun foo() { + foo(); + } + + // Finitely many types -- allowed. + // foo -> foo> -> foo> -> ... is valid + fun foo() { + foo>(); + } +} +} +``` + +Not allowed: + +```move +address 0x2 { +module m { + struct A {} + + // Infinitely many types -- NOT allowed. + // error! + // foo -> foo> -> foo>> -> ... + fun foo() { + foo>(); + } +} +} +``` + +```move +address 0x2 { +module n { + struct A {} + + // Infinitely many types -- NOT allowed. + // error! + // foo -> bar -> foo> + // -> bar, T2> -> foo, A> + // -> bar, A> -> foo, A>> + // -> ... + fun foo() { + bar(); + } + + fun bar { + foo>(); + } +} +} +``` + +Note, the check for type level recursions is based on a conservative analysis on the call sites and does NOT take control flow or runtime values into account. + +```move +address 0x2 { +module m { + struct A {} + + fun foo(n: u64) { + if (n > 0) { + foo>(n - 1); + }; + } +} +} +``` + +The function in the example above will technically terminate for any given input and therefore only creating finitely many types, but it is still considered invalid by Move's type system. \ No newline at end of file diff --git a/docs/content/references/ts-sdk/dapp-kit/index.mdx b/docs/content/references/ts-sdk/dapp-kit/index.mdx index eea63254587..bb1deb750f2 100644 --- a/docs/content/references/ts-sdk/dapp-kit/index.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/index.mdx @@ -1,5 +1,3 @@ -import AlphaNet from "../../../_snippets/alphanet.mdx"; - # Iota dApp Kit The Iota dApp Kit is a set of React components, hooks, and utilities to help you build a dApp for the @@ -30,7 +28,6 @@ To use the hooks and components in the dApp Kit, wrap your app with the provider following example. The props available on the providers are covered in more detail in their respective pages. - ```tsx import { createNetworkConfig, IotaClientProvider, WalletProvider } from '@iota/dapp-kit'; @@ -40,7 +37,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // Config options for the networks you want to connect to const { networkConfig } = createNetworkConfig({ localnet: { url: getFullnodeUrl('localnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }); const queryClient = new QueryClient(); diff --git a/docs/content/references/ts-sdk/dapp-kit/iota-client-provider.mdx b/docs/content/references/ts-sdk/dapp-kit/iota-client-provider.mdx index 397c7de7b2c..7bb5e400e03 100644 --- a/docs/content/references/ts-sdk/dapp-kit/iota-client-provider.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/iota-client-provider.mdx @@ -18,7 +18,7 @@ import { getFullnodeUrl } from '@iota/iota-sdk/client'; // Config options for the networks you want to connect to const { networkConfig } = createNetworkConfig({ localnet: { url: getFullnodeUrl('localnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }); function App() { @@ -55,7 +55,7 @@ import { useState } from 'react'; // Config options for the networks you want to connect to const { networkConfig } = createNetworkConfig({ localnet: { url: getFullnodeUrl('localnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }); function App() { @@ -86,7 +86,7 @@ import { getFullnodeUrl, IotaClient, IotaHTTPTransport } from '@iota/iota-sdk/cl // Config options for the networks you want to connect to const networks = { localnet: { url: getFullnodeUrl('localnet') }, - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, } satisfies Record; function App() { @@ -177,8 +177,8 @@ const { networkConfig, useNetworkVariable } = createNetworkConfig({ myMovePackageId: '0x123', }, }, - mainnet: { - url: getFullnodeUrl('mainnet'), + testnet: { + url: getFullnodeUrl('testnet'), variables: { myMovePackageId: '0x456', }, diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAccounts.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAccounts.mdx index 67d7627cb3b..b0a190c82e9 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAccounts.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAccounts.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet.mdx index 483a2f63747..6b5596dfffc 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useConnectWallet.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useConnectWallet.mdx index 5cf295496e1..1bd679a75e7 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useConnectWallet.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useConnectWallet.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount.mdx index 3854a29d55a..db1cdf7a0de 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet.mdx index 9f0128f9f1b..0c2c7996434 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet.mdx @@ -11,7 +11,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet.mdx index 8dc197a0787..dedf7ba93d9 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet.mdx @@ -11,7 +11,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransactionBlock.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransactionBlock.mdx index e470ad466ad..d57407b8607 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransactionBlock.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransactionBlock.mdx @@ -11,7 +11,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage.mdx index b0c8ed8a2ee..63fa0de140f 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignTransactionBlock.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignTransactionBlock.mdx index 5dcbf45a903..f0f9f82b441 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignTransactionBlock.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSignTransactionBlock.mdx @@ -11,7 +11,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount.mdx index dacd67ebddc..9abe519bad5 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount.mdx @@ -10,7 +10,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useWallets.mdx b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useWallets.mdx index 7a23f9fb686..67159b7c55a 100644 --- a/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useWallets.mdx +++ b/docs/content/references/ts-sdk/dapp-kit/wallet-hooks/useWallets.mdx @@ -11,7 +11,7 @@ function withProviders( // Work around server-side pre-rendering const queryClient = new QueryClient(); const networks = { - mainnet: { url: getFullnodeUrl('mainnet') }, + testnet: { url: getFullnodeUrl('testnet') }, }; return () => { diff --git a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx index 4a5006f01fb..329e5816c13 100644 --- a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx +++ b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx @@ -1,10 +1,7 @@ -import AlphaNet from "../../../_snippets/alphanet.mdx"; - # Some extended examples For these examples, assume you have the following data and functions available: - ```typescript // a constant for my type. @@ -15,9 +12,9 @@ const otherType = `${packageId}::other_module::OtherStruct<${packageId}::other_c // initialize a kioskClient. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), }), - network: Network.MAINNET, + network: Network.TESTNET, }); ``` diff --git a/docs/content/references/ts-sdk/kiosk/from-v1.mdx b/docs/content/references/ts-sdk/kiosk/from-v1.mdx index 3e697ce97ee..1ca2a06d3cf 100644 --- a/docs/content/references/ts-sdk/kiosk/from-v1.mdx +++ b/docs/content/references/ts-sdk/kiosk/from-v1.mdx @@ -47,9 +47,9 @@ Using the new SDK, you can build the same transaction as follows: /// You need to do this only once and re-use it in the application. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), }), - network: Network.MAINNET, + network: Network.TESTNET, }); const placeAndListToKiosk = async () => { @@ -102,9 +102,9 @@ Using the new SDK, you can build the same transaction as follows: /// You need to do this only once and re-use it in the application. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), }), - network: Network.MAINNET, + network: Network.TESTNET, }); const placeAndListToKiosk = async () => { @@ -129,7 +129,7 @@ import { queryTransferPolicy, purchaseAndResolvePolicies, place, testnetEnvironm import { IotaClient } from '@iota/iota-sdk/client'; const client = new IotaClient( - url: 'https://fullnode.testnet.iota.io:443', + url: 'https://api.testnet.iota.cafe:443', ); // The kiosk we're purchasing from. @@ -192,9 +192,9 @@ Using the new SDK, you can build the same transaction as follows: /// You need to do this only once and re-use it in the application. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), }), - network: Network.MAINNET, + network: Network.TESTNET, }); // An Item as returned from `kioskClient.getKiosk()` call. @@ -288,9 +288,9 @@ On the new SDK, the same transaction can be built as follows: /// You need to do this only once and re-use it in the application. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), }), - network: Network.MAINNET, + network: Network.TESTNET, }); const adjustPolicy = async () => { diff --git a/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx b/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx index 4039ef2ceee..73bea1b8fce 100644 --- a/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx +++ b/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx @@ -1,5 +1,3 @@ -import AlphaNet from "../../../../_snippets/alphanet.mdx"; - # Kiosk Client Kiosk Client is the base for all Kiosk SDK functionality. @@ -15,7 +13,6 @@ You can follow the example to create a `KioskClient`. The client currently suppo _IOTA Kiosk rules and extensions are not supported in Devnet due to network wipes (that would require constantly changing the package IDs)._ - ```typescript import { KioskClient, Network } from '@iota/kiosk'; diff --git a/docs/content/references/ts-sdk/typescript/cryptography/keypairs.mdx b/docs/content/references/ts-sdk/typescript/cryptography/keypairs.mdx index f25254186ef..77b6cdcb42d 100644 --- a/docs/content/references/ts-sdk/typescript/cryptography/keypairs.mdx +++ b/docs/content/references/ts-sdk/typescript/cryptography/keypairs.mdx @@ -1,9 +1,5 @@ -import AlphaNet from "../../../../_snippets/alphanet.mdx"; - # Key pairs - - The IOTA TypeScript SDK provides `Keypair` classes that handle logic for signing and verification using the cryptographic key pairs associated with a IOTA address. @@ -136,7 +132,7 @@ import { verifyPersonalMessageSignature } from '@iota/iota-sdk/verify'; const publicKey = await verifyPersonalMessageSignature(message, zkSignature, { client: new IotaGraphQLClient({ - url: 'https://iota-testnet.iota.org/graphql', + url: 'https://graphql.testnet.iota.cafe', }), }); ``` diff --git a/docs/content/references/ts-sdk/typescript/hello-iota.mdx b/docs/content/references/ts-sdk/typescript/hello-iota.mdx index 87d90f88986..cb0e4b79acc 100644 --- a/docs/content/references/ts-sdk/typescript/hello-iota.mdx +++ b/docs/content/references/ts-sdk/typescript/hello-iota.mdx @@ -1,5 +1,4 @@ import Faucet from '@site/../content/_snippets/faucet.mdx'; -import AlphaNet from "../../../_snippets/alphanet.mdx"; # Hello IOTA @@ -52,8 +51,6 @@ address. You must be on Devnet, Testnet, or a local network to use a faucet for Create a new `index.js` file in the root of your project with the following code. - - ```js import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { getFaucetHost, requestIotaFromFaucetv1 } from '@iota/iota-sdk/faucet'; diff --git a/docs/content/references/ts-sdk/typescript/index.mdx b/docs/content/references/ts-sdk/typescript/index.mdx index 888ce108894..5311b2a0349 100644 --- a/docs/content/references/ts-sdk/typescript/index.mdx +++ b/docs/content/references/ts-sdk/typescript/index.mdx @@ -1,4 +1,5 @@ -import AlphaNet from "../../../_snippets/alphanet.mdx"; +import NetworkInfo from "@site/src/components/NetworkInfo/index.tsx"; +import { Networks } from '@site/src/components/constant.tsx'; # IOTA TypeScript SDK Quick Start @@ -13,21 +14,26 @@ npm i @iota/iota-sdk ## Network locations - - The following table lists the locations for IOTA networks. -| Network | Full node | faucet | -| ------- | ------------------------------------- | ------------------------------------- | -| local | `http://127.0.0.1:9000` (default) | `http://127.0.0.1:9123/gas` (default) | -| Devnet | `https://fullnode.devnet.iota.io:443` | `https://faucet.iota-rebased-alphanet.iota.cafe/gas` | -| Testnet | - | - | -| Mainnet | - | - | +## IOTA Testnet + + + +## IOTA Devnet + + + +## IOTA Localnet + +To create a local IOTA network, you can refer to [Local Development](/developer/getting-started/local-network) page. + + :::warning Use dedicated nodes/shared services rather than public endpoints for production apps. The public -endpoints maintained by the IOTA Foundation (`fullnode..iota.io:443`) are rate-limited, and support +endpoints maintained by the IOTA Foundation (`api..iota.cafe:443`) are rate-limited, and support only 100 requests per 30 seconds or so. Do not use public endpoints in production applications with high traffic volume. diff --git a/docs/content/references/ts-sdk/typescript/iota-client.mdx b/docs/content/references/ts-sdk/typescript/iota-client.mdx index 3f5dc887c62..1a4210cdc33 100644 --- a/docs/content/references/ts-sdk/typescript/iota-client.mdx +++ b/docs/content/references/ts-sdk/typescript/iota-client.mdx @@ -95,7 +95,7 @@ import { WebSocket } from 'ws'; new IotaClient({ transport: new IotaHTTPTransport({ - url: getFullnodeUrl('mainnet'), + url: getFullnodeUrl('testnet'), // The typescript definitions may not match perfectly, casting to never avoids these minor incompatibilities WebSocketConstructor: WebSocket as never, }), diff --git a/docs/content/references/ts-sdk/typescript/owned-object-pool/examples.mdx b/docs/content/references/ts-sdk/typescript/owned-object-pool/examples.mdx index 3c574f20e89..1e96b495007 100644 --- a/docs/content/references/ts-sdk/typescript/owned-object-pool/examples.mdx +++ b/docs/content/references/ts-sdk/typescript/owned-object-pool/examples.mdx @@ -1,5 +1,4 @@ import Beta from '@site/../content/_snippets/iota-oop-beta.mdx' -import AlphaNet from "../../../../_snippets/alphanet.mdx"; # End-to-End Examples @@ -27,15 +26,13 @@ account's coins. Here is the code that creates the coins by splitting a single coin 20 times and transferring the new coins to your address: - - ```ts import { IotaClient } from '@iota/iota-sdk/client/'; import type { IotaObjectRef, IotaObjectResponse } from '@iota/iota-sdk/client/'; import { Transaction } from '@iota/iota-sdk/transactions'; const client = new IotaClient({ - url: 'https://fullnode.testnet.iota.io:443', + url: 'https://api.testnet.iota.cafe:443', }); const objectId: string = ''; // A diff --git a/docs/content/references/ts-sdk/typescript/owned-object-pool/local-development.mdx b/docs/content/references/ts-sdk/typescript/owned-object-pool/local-development.mdx index e6c9e9fd290..aec12c0308d 100644 --- a/docs/content/references/ts-sdk/typescript/owned-object-pool/local-development.mdx +++ b/docs/content/references/ts-sdk/typescript/owned-object-pool/local-development.mdx @@ -74,7 +74,7 @@ TEST_USER_SECRET= ... NFT_APP_PACKAGE_ID= ... NFT_APP_ADMIN_CAP= ... -# Example: "https://fullnode.testnet.iota.io:443" +# Example: "https://api.testnet.iota.cafe:443" IOTA_NODE= ... GET_WORKER_TIMEOUT_MS=1000 diff --git a/docs/content/references/ts-sdk/typescript/owned-object-pool/overview.mdx b/docs/content/references/ts-sdk/typescript/owned-object-pool/overview.mdx index 731e2ebb40b..7e44ffa7bc9 100644 --- a/docs/content/references/ts-sdk/typescript/owned-object-pool/overview.mdx +++ b/docs/content/references/ts-sdk/typescript/owned-object-pool/overview.mdx @@ -14,7 +14,7 @@ benefit of the lower latency those objects provide. On top of that, they are imp completely avoid because the transaction's gas coin is an owned object. Finally, the situation is exacerbated by -[gas smashing](../../../../developer/iota-101/transactions/gas-smashing.mdx) and the IOTA TypeScript SDK's +[coin-merging](../../../../developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx) and the IOTA TypeScript SDK's default coin selection logic, which uses all the `0x2::coin::Coin<0x2::iota::IOTA>` objects owned by an address for every transaction's gas payment. These defaults make sending transactions from your wallet straightforward (doing so automatically cleans up coin dust), but means that developers diff --git a/docs/content/sidebars/developer.js b/docs/content/sidebars/developer.js index 332d297d35f..62e73e36874 100644 --- a/docs/content/sidebars/developer.js +++ b/docs/content/sidebars/developer.js @@ -46,7 +46,19 @@ const developer = [ 'developer/iota-101/move-overview/strings', 'developer/iota-101/move-overview/collections', 'developer/iota-101/move-overview/init', + 'developer/iota-101/move-overview/visibility', 'developer/iota-101/move-overview/entry-functions', + { + type: 'category', + label: 'Structs and Abilities', + items: [ + 'developer/iota-101/move-overview/structs-and-abilities/struct', + 'developer/iota-101/move-overview/structs-and-abilities/copy', + 'developer/iota-101/move-overview/structs-and-abilities/drop', + 'developer/iota-101/move-overview/structs-and-abilities/key', + 'developer/iota-101/move-overview/structs-and-abilities/store', + ], + }, 'developer/iota-101/move-overview/one-time-witness', { type: 'category', @@ -58,6 +70,9 @@ const developer = [ 'developer/iota-101/move-overview/package-upgrades/custom-policies', ], }, + 'developer/iota-101/move-overview/ownership-scope', + 'developer/iota-101/move-overview/references', + 'developer/iota-101/move-overview/generics', { type: 'category', label: 'Patterns', @@ -94,6 +109,7 @@ const developer = [ 'developer/iota-101/objects/object-ownership/wrapped', ], }, + 'developer/iota-101/objects/uid-id', { type: 'category', label: 'Dynamic Fields', @@ -127,25 +143,32 @@ const developer = [ id: 'developer/iota-101/transactions/transactions', }, items: [ - 'developer/iota-101/transactions/sign-and-send-txn', + 'developer/iota-101/transactions/sign-and-send-transactions', { type: 'category', label: 'Sponsored Transactions', link: { type: 'doc', - id: 'developer/iota-101/transactions/sponsor-txn', + id: 'developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions', }, - items: ['developer/iota-101/transactions/sponsored-transactions'], + items: [ + 'developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions', + 'developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions'], }, - 'developer/iota-101/transactions/gas-smashing', { type: 'category', label: 'Working with PTBs', + link: { + type: 'doc', + id:'developer/iota-101/transactions/ptb/programmable-transaction-blocks-overview', + }, items: [ - 'developer/iota-101/transactions/ptb/prog-txn-blocks', - 'developer/iota-101/transactions/ptb/building-ptb', - 'developer/iota-101/transactions/ptb/coin-mgt', - 'developer/iota-101/transactions/ptb/simulating-refs', + 'developer/iota-101/transactions/ptb/programmable-transaction-blocks', + 'developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk', + 'developer/iota-101/transactions/ptb/simulating-references', + 'developer/iota-101/transactions/ptb/coin-management', + 'developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging', + ], }, ], @@ -357,503 +380,5 @@ const developer = [ items: ['developer/exchange-integration/exchange-integration'], }, 'developer/dev-cheat-sheet', - { - type: 'category', - label: 'Solidity/EVM Smart Contracts', - link: { - type: 'doc', - id: 'developer/iota-evm/introduction', - }, - items: [ - { - type: 'doc', - label: 'Introduction', - id: 'developer/iota-evm/introduction', - }, - { - type: 'category', - label: 'Getting Started', - items: [ - { - type: 'doc', - label: 'Languages & VMs', - id: 'developer/iota-evm/getting-started/languages-and-vms', - }, - 'developer/iota-evm/getting-started/quick-start', - 'developer/iota-evm/getting-started/compatibility', - { - type: 'doc', - label: 'Networks & Chains', - id: 'developer/iota-evm/getting-started/networks-and-chains', - }, - { - type: 'doc', - label: 'Tools', - id: 'developer/iota-evm/getting-started/tools', - }, - ], - }, - { - type: 'category', - label: 'How To', - items: [ - 'developer/iota-evm/how-tos/introduction', - { - type: 'doc', - label: 'Send Funds from L1 to L2', - id: 'developer/iota-evm/how-tos/send-funds-from-L1-to-L2', - }, - { - type: 'doc', - label: 'Create a Basic Contract', - id: 'developer/iota-evm/how-tos/create-a-basic-contract', - }, - { - type: 'doc', - label: 'Deploy a Smart Contract', - id: 'developer/iota-evm/how-tos/deploy-a-smart-contract', - }, - { - type: 'doc', - label: 'Create Custom Tokens - ERC20', - id: 'developer/iota-evm/how-tos/ERC20', - }, - { - type: 'doc', - label: 'Send ERC20 Tokens Across Chains', - id: 'developer/iota-evm/how-tos/send-ERC20-across-chains', - }, - { - type: 'doc', - label: 'Create NFTs - ERC721', - id: 'developer/iota-evm/how-tos/ERC721', - }, - { - type: 'doc', - label: 'Send NFTs Across Chains', - id: 'developer/iota-evm/how-tos/send-NFTs-across-chains', - }, - { - type: 'doc', - label: 'Test Smart Contracts', - id: 'developer/iota-evm/how-tos/test-smart-contracts', - }, - { - type: 'category', - label: 'Interact with the Core Contracts', - items: [ - { - type: 'doc', - label: 'Introduction', - id: 'developer/iota-evm/how-tos/core-contracts/introduction', - }, - { - type: 'category', - label: 'Basics', - items: [ - { - type: 'doc', - label: 'Get Native Assets Balance', - id: 'developer/iota-evm/how-tos/core-contracts/basics/get-balance', - }, - { - type: 'category', - label: 'Allowance', - items: [ - { - type: 'doc', - label: 'Allow', - id: 'developer/iota-evm/how-tos/core-contracts/basics/allowance/allow', - }, - { - type: 'doc', - label: 'Get Allowance', - id: 'developer/iota-evm/how-tos/core-contracts/basics/allowance/get-allowance', - }, - { - type: 'doc', - label: 'Take Allowance', - id: 'developer/iota-evm/how-tos/core-contracts/basics/allowance/take-allowance', - }, - ], - }, - { - type: 'doc', - label: 'Send Assets to L1', - id: 'developer/iota-evm/how-tos/core-contracts/basics/send-assets-to-l1', - }, - ], - }, - { - type: 'category', - label: 'Token', - items: [ - { - label: 'Introduction', - type: 'doc', - id: 'developer/iota-evm/how-tos/core-contracts/token/introduction', - }, - { - type: 'doc', - label: 'Create a Native Token', - id: 'developer/iota-evm/how-tos/core-contracts/token/create-native-token', - }, - { - type: 'doc', - label: 'Mint Native Tokens', - id: 'developer/iota-evm/how-tos/core-contracts/token/mint-token', - }, - { - type: 'doc', - label: 'Custom ERC20 Functions', - id: 'developer/iota-evm/how-tos/core-contracts/token/erc20-native-token', - }, - { - type: 'doc', - label: 'Create a Foundry', - id: 'developer/iota-evm/how-tos/core-contracts/token/create-foundry', - }, - { - type: 'doc', - label: 'Register Token as ERC20', - id: 'developer/iota-evm/how-tos/core-contracts/token/register-token', - }, - ], - }, - { - type: 'category', - label: 'NFT', - items: [ - { - label: 'Introduction', - type: 'doc', - id: 'developer/iota-evm/how-tos/core-contracts/nft/introduction', - }, - { - type: 'doc', - label: 'Mint an NFT', - id: 'developer/iota-evm/how-tos/core-contracts/nft/mint-nft', - }, - { - type: 'doc', - label: 'Use as ERC721', - id: 'developer/iota-evm/how-tos/core-contracts/nft/use-as-erc721', - }, - ], - }, - { - type: 'doc', - label: 'Get Randomness on L2', - id: 'developer/iota-evm/how-tos/core-contracts/get-randomness-on-l2', - }, - { - type: 'doc', - label: 'Call and Call View', - id: 'developer/iota-evm/how-tos/core-contracts/call-view', - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Explanations', - items: [ - { - type: 'doc', - label: 'Anatomy of a Smart Contract', - id: 'developer/iota-evm/explanations/smart-contract-anatomy', - }, - { - type: 'doc', - label: 'Sandbox Interface', - id: 'developer/iota-evm/explanations/sandbox', - }, - { - type: 'doc', - label: 'Calling a Smart Contract', - id: 'developer/iota-evm/explanations/invocation', - }, - { - type: 'doc', - label: 'State, Transitions and State Anchoring', - id: 'developer/iota-evm/explanations/states', - }, - { - type: 'doc', - label: 'State manager', - id: 'developer/iota-evm/explanations/state_manager', - }, - { - type: 'doc', - label: 'Validators and Access Nodes', - id: 'developer/iota-evm/explanations/validators', - }, - { - type: 'doc', - label: 'Consensus', - id: 'developer/iota-evm/explanations/consensus', - }, - { - type: 'doc', - label: 'How Accounts Work', - id: 'developer/iota-evm/explanations/how-accounts-work', - }, - { - type: 'doc', - label: 'Core Contracts', - id: 'references/iota-evm/core-contracts/overview', - }, - ], - }, - { - type: 'category', - label: 'Test with Solo', - items: [ - { - label: 'Getting Started', - id: 'developer/iota-evm/solo/getting-started', - type: 'doc', - }, - { - type: 'category', - label: 'How To', - items: [ - { - type: 'doc', - label: 'First Example', - id: 'developer/iota-evm/solo/how-tos/first-example', - }, - { - type: 'doc', - label: 'The L1 Ledger', - id: 'developer/iota-evm/solo/how-tos/the-l1-ledger', - }, - { - type: 'doc', - label: 'Deploy a Smart Contract', - id: 'developer/iota-evm/solo/how-tos/deploying-sc', - }, - { - type: 'doc', - label: 'Invoke a Smart Contract', - id: 'developer/iota-evm/solo/how-tos/invoking-sc', - }, - { - type: 'doc', - label: 'Call a View', - id: 'developer/iota-evm/solo/how-tos/view-sc', - }, - { - type: 'doc', - label: 'Error Handling', - id: 'developer/iota-evm/solo/how-tos/error-handling', - }, - { - type: 'doc', - label: 'Accounts', - id: 'developer/iota-evm/solo/how-tos/the-l2-ledger', - }, - { - type: 'doc', - label: 'Test Smart Contracts', - id: 'developer/iota-evm/solo/how-tos/test', - }, - { - type: 'doc', - label: 'Example Tests', - id: 'developer/iota-evm/solo/how-tos/examples', - }, - { - type: 'doc', - label: 'Colored Tokens and Time Locks', - id: 'developer/iota-evm/solo/how-tos/timelock', - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Wasm - Schema Tool', - items: [ - { - type: 'doc', - label: 'The Schema Tool', - id: 'developer/iota-evm/schema/introduction', - }, - { - type: 'doc', - label: 'Data Access Proxies', - id: 'developer/iota-evm/schema/proxies', - }, - { - type: 'category', - label: 'How To', - items: [ - { - type: 'doc', - label: 'Create a Schema', - id: 'developer/iota-evm/schema/how-tos/usage', - }, - { - type: 'doc', - label: 'Define the State', - id: 'developer/iota-evm/schema/how-tos/state', - }, - { - type: 'doc', - label: 'Use Structured Data Types', - id: 'developer/iota-evm/schema/how-tos/structs', - }, - { - type: 'doc', - label: 'Generate Type Definitions', - id: 'developer/iota-evm/schema/how-tos/typedefs', - }, - { - type: 'doc', - label: 'Trigger Events', - id: 'developer/iota-evm/schema/how-tos/events', - }, - { - type: 'doc', - label: 'Define Functions', - id: 'developer/iota-evm/schema/how-tos/funcs', - }, - { - type: 'doc', - label: 'Limit Access', - id: 'developer/iota-evm/schema/how-tos/access', - }, - { - type: 'doc', - label: 'Define Function Parameters', - id: 'developer/iota-evm/schema/how-tos/params', - }, - { - type: 'doc', - label: 'Define Function Results', - id: 'developer/iota-evm/schema/how-tos/results', - }, - { - type: 'doc', - label: 'Use Thunk Functions', - id: 'developer/iota-evm/schema/how-tos/thunks', - }, - { - type: 'doc', - label: 'Use View-Only Functions', - id: 'developer/iota-evm/schema/how-tos/views', - }, - { - type: 'doc', - label: 'Initialize a Smart Contract', - id: 'developer/iota-evm/schema/how-tos/init', - }, - { - type: 'doc', - label: 'Transfer Tokens', - id: 'developer/iota-evm/schema/how-tos/transfers', - }, - { - type: 'doc', - label: 'Add Function Descriptors', - id: 'developer/iota-evm/schema/how-tos/funcdesc', - }, - { - type: 'doc', - label: 'Call Functions', - id: 'developer/iota-evm/schema/how-tos/call', - }, - { - type: 'doc', - label: 'Post Asynchronous Requests', - id: 'developer/iota-evm/schema/how-tos/post', - }, - ], - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Decentralized Identity', - link: { - type: 'doc', - id: 'developer/iota-identity/welcome', - }, - items: [ - { - type: 'doc', - id: 'developer/iota-identity/welcome', - label: 'Welcome', - }, - { - type: 'category', - label: 'Getting Started', - collapsed: false, - items: [ - 'developer/iota-identity/getting-started/rust', - 'developer/iota-identity/getting-started/wasm', - ], - }, - { - type: 'category', - label: 'Explanations', - items: [ - 'developer/iota-identity/explanations/decentralized-identifiers', - 'developer/iota-identity/explanations/verifiable-credentials', - 'developer/iota-identity/explanations/verifiable-presentations', - 'developer/iota-identity/explanations/about-alias-outputs', - ], - }, - { - type: 'category', - label: 'How To', - items: [ - { - type: 'category', - label: 'Decentralized Identifiers (DID)', - items: [ - 'developer/iota-identity/how-tos/decentralized-identifiers/create', - 'developer/iota-identity/how-tos/decentralized-identifiers/update', - 'developer/iota-identity/how-tos/decentralized-identifiers/resolve', - 'developer/iota-identity/how-tos/decentralized-identifiers/delete', - ], - }, - { - type: 'category', - label: 'Verifiable Credentials', - items: [ - 'developer/iota-identity/how-tos/verifiable-credentials/create', - 'developer/iota-identity/how-tos/verifiable-credentials/revocation', - 'developer/iota-identity/how-tos/verifiable-credentials/selective-disclosure', - 'developer/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure', - ], - }, - { - type: 'category', - label: 'Verifiable Presentations', - items: [ - 'developer/iota-identity/how-tos/verifiable-presentations/create-and-validate', - ], - }, - { - type: 'category', - label: 'Domain Linkage', - items: ['developer/iota-identity/how-tos/domain-linkage/create-and-verify'], - }, - 'developer/iota-identity/how-tos/key-storage', - ], - }, - 'developer/iota-identity/glossary', - 'developer/iota-identity/contribute', - 'developer/iota-identity/workflow', - 'developer/iota-identity/contact', - 'developer/iota-identity/faq', - ], - }, ]; module.exports = developer; diff --git a/docs/content/sidebars/operator.js b/docs/content/sidebars/operator.js index 6aed36b0e64..a244da04ff4 100644 --- a/docs/content/sidebars/operator.js +++ b/docs/content/sidebars/operator.js @@ -32,61 +32,5 @@ const operator = [ 'operator/telemetry/iota-telemetry', ], }, - { - type: 'category', - label: 'IOTA EVM Network', - link: { - type: 'doc', - id: 'operator/iota-evm/how-tos/running-a-node', - }, - items: [ - { - type: 'category', - label: 'How To', - collapsed: false, - items: [ - { - type: 'doc', - id: 'operator/iota-evm/how-tos/running-a-node', - label: 'Run a Node', - }, - { - type: 'doc', - id: 'operator/iota-evm/how-tos/running-an-access-node', - label: 'Run an Access Node', - }, - { - id: 'operator/iota-evm/how-tos/wasp-cli', - label: 'Configure wasp-cli', - type: 'doc', - }, - { - id: 'operator/iota-evm/how-tos/setting-up-a-chain', - label: 'Set Up a Chain', - type: 'doc', - }, - { - id: 'operator/iota-evm/how-tos/chain-management', - label: 'Manage a Chain', - type: 'doc', - }, - ], - }, - { - type: 'category', - label: 'Reference', - items: [ - { - type: 'doc', - id: 'operator/iota-evm/reference/configuration', - }, - { - type: 'doc', - id: 'operator/iota-evm/reference/metrics', - }, - ], - }, - ], - }, ]; module.exports = operator; diff --git a/docs/content/sidebars/references.js b/docs/content/sidebars/references.js index 142c82dc1a0..fbfcb147ed0 100644 --- a/docs/content/sidebars/references.js +++ b/docs/content/sidebars/references.js @@ -18,7 +18,7 @@ const references = [ id: 'references/iota-api', }, items: [ - /*{ + { type: 'category', label: 'GraphQL', link: { @@ -31,7 +31,7 @@ const references = [ dirName: 'references/iota-api/iota-graphql/reference', }, ], - },*/ + }, { type: 'link', label: 'JSON-RPC', @@ -223,6 +223,8 @@ const references = [ }, 'references/move/move-toml', 'references/move/move-lock', + 'references/move/abilities', + 'references/move/generics', { type: 'link', label: 'Move Language (GitHub)', @@ -231,133 +233,6 @@ const references = [ }, ], }, - { - type: 'category', - label: 'IOTA EVM', - items: [ - 'references/iota-evm/json-rpc-spec', - { - type: 'category', - label: 'Magic Contract', - items: [ - { - type: 'autogenerated', - dirName: 'references/iota-evm/magic-contract', - }, - ], - }, - { - type: 'category', - label: 'Core Contracts', - items: [ - { - type: 'doc', - label: 'Overview', - id: 'references/iota-evm/core-contracts/overview', - }, - { - type: 'doc', - label: 'root', - id: 'references/iota-evm/core-contracts/root', - }, - { - type: 'doc', - label: 'accounts', - id: 'references/iota-evm/core-contracts/accounts', - }, - { - type: 'doc', - label: 'blob', - id: 'references/iota-evm/core-contracts/blob', - }, - { - type: 'doc', - label: 'blocklog', - id: 'references/iota-evm/core-contracts/blocklog', - }, - { - type: 'doc', - label: 'governance', - id: 'references/iota-evm/core-contracts/governance', - }, - { - type: 'doc', - label: 'errors', - id: 'references/iota-evm/core-contracts/errors', - }, - { - type: 'doc', - label: 'EVM', - id: 'references/iota-evm/core-contracts/evm', - }, - ], - }, - { - type: 'category', - label: 'ISC Utilities', - items: [ - { - type: 'autogenerated', - dirName: 'references/iota-evm/iscutils', - }, - ], - }, - { - type: 'doc', - label: 'WasmLib Data Types', - id: 'references/iota-evm/wasm-lib-data-types', - }, - ], - }, - { - type: 'category', - label: 'IOTA Identity', - link: { - type: 'doc', - id: 'references/iota-identity/overview', - }, - items: [ - 'references/iota-identity/overview', - 'references/iota-identity/iota-did-method-spec', - 'references/iota-identity/revocation-bitmap-2022', - 'references/iota-identity/revocation-timeframe-2024', - { - type: 'category', - label: 'Wasm', - items: [ - { - type: 'autogenerated', - dirName: 'references/iota-identity/wasm', - }, - ], - }, - { - type: 'link', - label: 'Rust', - href: 'https://docs.rs/identity_iota/latest/identity_iota/index.html', - description: 'IOTA Identity Rust Documentation', - }, - ], - }, - { - type: 'category', - label: 'Expert topics', - items: [ - { - type: 'category', - label: 'Execution Architecture', - link: { - type: 'doc', - id: 'references/execution-architecture/execution-layer', - }, - items: [ - 'references/execution-architecture/iota-execution', - 'references/execution-architecture/adapter', - 'references/execution-architecture/natives', - ], - }, - ], - }, { type: 'category', label: 'Expert topics', diff --git a/docs/examples/move/ctf/README.md b/docs/examples/move/ctf/README.md index 700e6412b6f..0912c0fe00b 100644 --- a/docs/examples/move/ctf/README.md +++ b/docs/examples/move/ctf/README.md @@ -2,7 +2,7 @@ ## Trying the Challenges -The challneges are already deployed on the IOTA alphanet. To get started, you need to start with reading the [CTF introduction](../../docs/content/developer/iota-move-ctf/introduction.mdx), follow its steps to interact with the challenges, and capture the flags. +The challenges are already deployed on the IOTA Testnet. To get started, you need to start with reading the [CTF introduction](../../docs/content/developer/iota-move-ctf/introduction.mdx), follow its steps to interact with the challenges, and capture the flags. ## Deploying the challneges yourself (Not Required) diff --git a/docs/examples/move/move-overview/copy-ability.move b/docs/examples/move/move-overview/copy-ability.move new file mode 100644 index 00000000000..71eb25e1f4a --- /dev/null +++ b/docs/examples/move/move-overview/copy-ability.move @@ -0,0 +1,27 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_variable)] +module book::copy_ability; + +// ANCHOR: copyable +public struct Copyable has copy {} +// ANCHOR_END: copyable + +#[test] fun test_copy() { + +// ANCHOR: copyable_test +let a = Copyable {}; +let b = a; // `a` is copied to `b` +let c = *&b; // explicit copy via dereference operator + +let Copyable {} = a; // doesn't have `drop` +let Copyable {} = b; // doesn't have `drop` +let Copyable {} = c; // doesn't have `drop` +// ANCHOR_END: copyable_test +} + +// ANCHOR: copy_drop +public struct Value has copy, drop {} +// ANCHOR_END: copy_drop \ No newline at end of file diff --git a/docs/examples/move/move-overview/drop-ability.move b/docs/examples/move/move-overview/drop-ability.move new file mode 100644 index 00000000000..8849777afca --- /dev/null +++ b/docs/examples/move/move-overview/drop-ability.move @@ -0,0 +1,27 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: main +module book::drop_ability; + +/// This struct has the `drop` ability. +public struct IgnoreMe has drop { + a: u8, + b: u8, +} + +/// This struct does not have the `drop` ability. +public struct NoDrop {} + +#[test] +// Create an instance of the `IgnoreMe` struct and ignore it. +// Even though we constructed the instance, we don't need to unpack it. +fun test_ignore() { + let no_drop = NoDrop {}; + let _ = IgnoreMe { a: 1, b: 2 }; // no need to unpack + + // The value must be unpacked for the code to compile. + let NoDrop {} = no_drop; // OK +} +// ANCHOR_END: main \ No newline at end of file diff --git a/docs/examples/move/move-overview/generics.move b/docs/examples/move/move-overview/generics.move new file mode 100644 index 00000000000..c0068862a39 --- /dev/null +++ b/docs/examples/move/move-overview/generics.move @@ -0,0 +1,153 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +#[allow(unused_variable, unused_field)] +module book::generics; + +// ANCHOR: container +/// Container for any type `T`. +public struct Container has drop { + value: T, +} + +/// Function that creates a new `Container` with a generic value `T`. +public fun new(value: T): Container { + Container { value } +} +// ANCHOR_END: container + +// ANCHOR: test_container +#[test] +fun test_container() { + // these three lines are equivalent + let container: Container = new(10); // type inference + let container = new(10); // create a new `Container` with a `u8` value + let container = new(10u8); + + assert!(container.value == 10, 0x0); + + // Value can be ignored only if it has the `drop` ability. + let Container { value: _ } = container; +} +// ANCHOR_END: test_container + +// ANCHOR: pair +/// A pair of values of any type `T` and `U`. +public struct Pair { + first: T, + second: U, +} + +/// Function that creates a new `Pair` with two generic values `T` and `U`. +public fun new_pair(first: T, second: U): Pair { + Pair { first, second } +} +// ANCHOR_END: pair + +// ANCHOR: test_pair +#[test] +fun test_generic() { + // these three lines are equivalent + let pair_1: Pair = new_pair(10, true); // type inference + let pair_2 = new_pair(10, true); // create a new `Pair` with a `u8` and `bool` values + let pair_3 = new_pair(10u8, true); + + assert!(pair_1.first == 10, 0x0); + assert!(pair_1.second, 0x0); + + // Unpacking is identical. + let Pair { first: _, second: _ } = pair_1; + let Pair { first: _, second: _ } = pair_2; + let Pair { first: _, second: _ } = pair_3; + +} +// ANCHOR_END: test_pair + +// ANCHOR: test_pair_swap +#[test] +fun test_swap_type_params() { + let pair1: Pair = new_pair(10u8, true); + let pair2: Pair = new_pair(true, 10u8); + + // this line will not compile + // assert!(pair1 == pair2, 0x0); + + let Pair { first: pf1, second: ps1 } = pair1; // first1: u8, second1: bool + let Pair { first: pf2, second: ps2 } = pair2; // first2: bool, second2: u8 + + assert!(pf1 == ps2); // 10 == 10 + assert!(ps1 == pf2); // true == true +} +// ANCHOR_END: test_pair_swap + +use std::string::String; + +// ANCHOR: user +/// A user record with name, age, and some generic metadata +public struct User { + name: String, + age: u8, + /// Varies depending on application. + metadata: T, +} +// ANCHOR_END: user + +// ANCHOR: update_user +/// Updates the name of the user. +public fun update_name(user: &mut User, name: String) { + user.name = name; +} + +/// Updates the age of the user. +public fun update_age(user: &mut User, age: u8) { + user.age = age; +} +// ANCHOR_END: update_user + +// ANCHOR: phantom +/// A generic type with a phantom type parameter. +public struct Coin { + value: u64 +} +// ANCHOR_END: phantom + +// ANCHOR: test_phantom +public struct USD {} +public struct EUR {} + +#[test] +fun test_phantom_type() { + let coin1: Coin = Coin { value: 10 }; + let coin2: Coin = Coin { value: 20 }; + + // Unpacking is identical because the phantom type parameter is not used. + let Coin { value: _ } = coin1; + let Coin { value: _ } = coin2; +} +// ANCHOR_END: test_phantom + +// ANCHOR: constraints +/// A generic type with a type parameter that has the `drop` ability. +public struct Droppable { + value: T, +} + +/// A generic struct with a type parameter that has the `copy` and `drop` abilities. +public struct CopyableDroppable { + value: T, // T must have the `copy` and `drop` abilities +} +// ANCHOR_END: constraints + +// ANCHOR: test_constraints +/// Type without any abilities. +public struct NoAbilities {} + +#[test] +fun test_constraints() { + // Fails - `NoAbilities` does not have the `drop` ability + // let droppable = Droppable { value: 10 }; + + // Fails - `NoAbilities` does not have the `copy` and `drop` abilities + // let copyable_droppable = CopyableDroppable { value: 10 }; +} +// ANCHOR_END: test_constraints \ No newline at end of file diff --git a/docs/examples/move/move-overview/references.move b/docs/examples/move/move-overview/references.move new file mode 100644 index 00000000000..afa9eb66b2c --- /dev/null +++ b/docs/examples/move/move-overview/references.move @@ -0,0 +1,66 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +module book::metro_pass { + + /// Error code for when the card is empty. + const ENoUses: u64 = 0; + + /// Number of uses for a metro pass card. + const USES: u8 = 3; + + /// A metro pass card + public struct Card { uses: u8 } + + /// Purchase a metro pass card. + public fun purchase(/* pass a Coin */): Card { + Card { uses: USES } + } + + /// Show the metro pass card to the inspector. + public fun is_valid(card: &Card): bool { + card.uses > 0 + } + + /// Use the metro pass card at the turnstile to enter the metro. + public fun enter_metro(card: &mut Card) { + assert!(card.uses > 0, ENoUses); + card.uses = card.uses - 1; + } + + /// Recycle the metro pass card. + public fun recycle(card: Card) { + assert!(card.uses == 0, ENoUses); + let Card { uses: _ } = card; + } + + #[test] + fun test_card() { + // declaring variable as mutable because we modify it + let mut card = purchase(); + + enter_metro(&mut card); + + assert!(is_valid(&card)); // read the card! + + enter_metro(&mut card); // modify the card but don't move it + enter_metro(&mut card); // modify the card but don't move it + + recycle(card); // move the card out of the scope + } + + #[test] + fun test_card_2024() { + // declaring variable as mutable because we modify it + let mut card = purchase(); + + card.enter_metro(); // modify the card but don't move it + assert!(card.is_valid()); // read the card! + + card.enter_metro(); // modify the card but don't move it + card.enter_metro(); // modify the card but don't move it + + card.recycle(); // move the card out of the scope + } +} diff --git a/docs/examples/move/move-overview/struct.move b/docs/examples/move/move-overview/struct.move new file mode 100644 index 00000000000..51007c09164 --- /dev/null +++ b/docs/examples/move/move-overview/struct.move @@ -0,0 +1,68 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_variable, unused_field)] +module book::struct_syntax; + +use std::string::{Self, String}; + +// ANCHOR: def +/// A struct representing an artist. +public struct Artist { + /// The name of the artist. + name: String, +} + +/// A struct representing a music record. +public struct Record { + /// The title of the record. + title: String, + /// The artist of the record. Uses the `Artist` type. + artist: Artist, + /// The year the record was released. + year: u16, + /// Whether the record is a debut album. + is_debut: bool, + /// The edition of the record. + edition: Option, +} +// ANCHOR_END: def + +#[test] fun test_pack_unpack() { + +// ANCHOR: pack +let mut artist = Artist { + name: b"The Beatles".to_string() +}; +// ANCHOR_END: pack + +// ANCHOR: access +// Access the `name` field of the `Artist` struct. +let artist_name = artist.name; + +// Access a field of the `Artist` struct. +assert!(artist.name == b"The Beatles".to_string()); + +// Mutate the `name` field of the `Artist` struct. +artist.name = b"Led Zeppelin".to_string(); + +// Check that the `name` field has been mutated. +assert!(artist.name == b"Led Zeppelin".to_string()); +// ANCHOR_END: access + +// ANCHOR: unpack +// Unpack the `Artist` struct and create a new variable `name` +// with the value of the `name` field. +let Artist { name } = artist; +// ANCHOR_END: unpack + +let artist = Artist { + name: b"The Beatles".to_string() +}; + +// ANCHOR: unpack_ignore +// Unpack the `Artist` struct and ignore the `name` field. +let Artist { name: _ } = artist; +// ANCHOR_END: unpack_ignore +} \ No newline at end of file diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index ff0affda9ff..a789f2b0a80 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -18,7 +18,7 @@ const config = { favicon: "/icons/favicon.ico", // Set the production url of your site here - url: "https://docs.iota.io", + url: "https://docs.iota.org", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' baseUrl: "/", diff --git a/docs/site/package.json b/docs/site/package.json index c522fcd8d3e..1e6741e64c2 100644 --- a/docs/site/package.json +++ b/docs/site/package.json @@ -4,8 +4,8 @@ "private": true, "scripts": { "docusaurus": "docusaurus", - "dev": "UPDATE=1 cargo test --manifest-path ../../crates/iota-framework/Cargo.toml; docusaurus graphql-to-doc; rm '../content/references/iota-api/iota-graphql/reference/generated.md'; pnpm run download-references; docusaurus start", - "build": "UPDATE=1 cargo test --manifest-path ../../crates/iota-framework/Cargo.toml; docusaurus graphql-to-doc; rm '../content/references/iota-api/iota-graphql/reference/generated.md'; pnpm run download-references; docusaurus build", + "dev": "UPDATE=1 cargo test --manifest-path ../../crates/iota-framework/Cargo.toml; docusaurus graphql-to-doc; rm '../content/references/iota-api/iota-graphql/reference/generated.md'; docusaurus start", + "build": "UPDATE=1 cargo test --manifest-path ../../crates/iota-framework/Cargo.toml; docusaurus graphql-to-doc; rm '../content/references/iota-api/iota-graphql/reference/generated.md'; docusaurus build", "download-references": "git clean -Xdf ../content/references/iota-evm/; ./scripts/get-iota-evm-references.sh; git clean -Xdf ../content/references/iota-identity/; ./scripts/get-iota-identity-references.sh", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", diff --git a/docs/site/src/components/API/api-ref/networkselect.js b/docs/site/src/components/API/api-ref/networkselect.js index f1918387e41..8132c3e125d 100644 --- a/docs/site/src/components/API/api-ref/networkselect.js +++ b/docs/site/src/components/API/api-ref/networkselect.js @@ -7,18 +7,18 @@ import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment"; import { Select, MenuItem, FormControl, InputLabel } from "@mui/material"; import { StyledEngineProvider } from "@mui/material/styles"; -const NETWORKS = ["Devnet", "Testnet", "Mainnet"]; +const NETWORKS = ["Devnet", "Testnet"]; const NetworkSelect = () => { const [selection, setSelection] = useState(() => { if (ExecutionEnvironment.canUseDOM) { const network = localStorage.getItem("RPC"); if (network === null) { - return "mainnet"; + return "testnet"; } return localStorage.getItem("RPC"); } else { - return "mainnet"; + return "testnet"; } }); @@ -38,18 +38,18 @@ const NetworkSelect = () => { {`RPC: https://fullnode.${selection.toLowerCase()}.iota.io:443`} + >{`RPC: https://api.${selection.toLowerCase()}.iota.cafe:443`} diff --git a/docs/site/src/components/CTF/ctf-verifier.tsx b/docs/site/src/components/CTF/ctf-verifier.tsx index cc25c579746..530ddf2d44f 100644 --- a/docs/site/src/components/CTF/ctf-verifier.tsx +++ b/docs/site/src/components/CTF/ctf-verifier.tsx @@ -13,8 +13,7 @@ interface ChallengeVerifierProps { // Define network configurations const NETWORKS = { - devnet: { url: getFullnodeUrl('devnet') }, - alphanet: { url: 'https://api.iota-rebased-alphanet.iota.cafe:443' }, + testnet: { url: getFullnodeUrl('testnet') }, }; // Main ChallengeVerifier component @@ -30,7 +29,7 @@ const ChallengeVerifier: React.FC = ({ expectedObjectTyp setCoins(null); try { - const client = new IotaClient({ url: NETWORKS.alphanet.url }); + const client = new IotaClient({ url: NETWORKS.testnet.url }); const result = await client.getObject({ id: inputText, options: { showType: true } }); const message = result.data.type === expectedObjectType diff --git a/docs/site/src/components/constant.tsx b/docs/site/src/components/constant.tsx index 4c5a3e40d49..7639845f062 100644 --- a/docs/site/src/components/constant.tsx +++ b/docs/site/src/components/constant.tsx @@ -166,21 +166,21 @@ export const Networks = { }, iota_move_testnet: { baseToken: 'IOTA Token (no value)', - jsonRpcUrl: 'https://api.iota-rebased-alphanet.iota.cafe', - jsonRpcWebsocketUrl:'wss://api.iota-rebased-alphanet.iota.cafe', - indexerRpc: 'https://indexer.iota-rebased-alphanet.iota.cafe', - graphqlRpc: 'https://graphql.iota-rebased-alphanet.iota.cafe', - faucetUrl: 'https://api.iota-rebased-alphanet.iota.cafe/gas', - explorerUrl: 'https://explorer.iota.cafe/?network=alphanet' + jsonRpcUrl: 'https://api.testnet.iota.cafe', + jsonRpcWebsocketUrl:'wss://api.testnet.iota.cafe', + indexerRpc: 'https://indexer.testnet.iota.cafe', + graphqlRpc: 'https://graphql.testnet.iota.cafe', + faucetUrl: 'https://faucet.testnet.iota.cafe', + explorerUrl: 'https://explorer.iota.cafe/?network=testnet' }, iota_move_devnet: { baseToken: 'IOTA Token (no value)', - jsonRpcUrl: 'jsonRpcUrl placeholder', - jsonRpcWebsocketUrl:'jsonRpcWebsocketUrl placeholder', - indexerRpc: 'indexerRpc placeholder', - graphqlRpc: 'graphqlRpc placeholder', - faucetUrl: 'faucetUrl placeholder', - explorerUrl: 'explorerUrl placeholder' + jsonRpcUrl: 'https://api.devnet.iota.cafe', + jsonRpcWebsocketUrl:'wss://api.devnet.iota.cafe', + indexerRpc: 'https://indexer.devnet.iota.cafe', + graphqlRpc: 'https://graphql.devnet.iota.cafe', + faucetUrl: 'https://faucet.devnet.iota.cafe', + explorerUrl: 'https://explorer.iota.cafe/?network=devnet' }, iota_localnet: { baseToken:"IOTA Token", diff --git a/docs/site/src/pages/index.js b/docs/site/src/pages/index.js index 8a5c6126658..ba011c0c546 100644 --- a/docs/site/src/pages/index.js +++ b/docs/site/src/pages/index.js @@ -152,7 +152,7 @@ export default function Home() { > IOTA ecosystem directory - + IOTA blog { } // Download Mainnet OpenRPC spec -downloadFile("mainnet"); +// downloadFile("mainnet"); // Download Testnet OpenRPC spec downloadFile("testnet"); diff --git a/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/sponsored-txn.png b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/sponsored-txn.png new file mode 100644 index 00000000000..87d82ea870f Binary files /dev/null and b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/sponsored-txn.png differ diff --git a/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/user-proposed-txn.png b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/user-proposed-txn.png new file mode 100644 index 00000000000..bf41cde4e1f Binary files /dev/null and b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/user-proposed-txn.png differ diff --git a/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/wildcard-txn.png b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/wildcard-txn.png new file mode 100644 index 00000000000..9521b65d653 Binary files /dev/null and b/docs/site/static/img/developer/iota-101/transactions/sponsored-transactions/wildcard-txn.png differ diff --git a/docs/site/static/json/developer/iota-101/transactions/ptb/building-ptb.json b/docs/site/static/json/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/ptb/building-ptb.json rename to docs/site/static/json/developer/iota-101/transactions/ptb/building-programmable-transaction-blocks-ts-sdk.json diff --git a/docs/site/static/json/developer/iota-101/transactions/ptb/coin-mgt.json b/docs/site/static/json/developer/iota-101/transactions/ptb/coin-management.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/ptb/coin-mgt.json rename to docs/site/static/json/developer/iota-101/transactions/ptb/coin-management.json diff --git a/docs/site/static/json/developer/iota-101/transactions/gas-smashing.json b/docs/site/static/json/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/gas-smashing.json rename to docs/site/static/json/developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.json diff --git a/docs/site/static/json/developer/iota-101/transactions/ptb/prog-txn-blocks.json b/docs/site/static/json/developer/iota-101/transactions/ptb/programmable-transaction-blocks.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/ptb/prog-txn-blocks.json rename to docs/site/static/json/developer/iota-101/transactions/ptb/programmable-transaction-blocks.json diff --git a/docs/site/static/json/developer/iota-101/transactions/ptb/simulating-refs.json b/docs/site/static/json/developer/iota-101/transactions/ptb/simulating-references.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/ptb/simulating-refs.json rename to docs/site/static/json/developer/iota-101/transactions/ptb/simulating-references.json diff --git a/docs/site/static/json/developer/iota-101/transactions/sign-and-send-txn.json b/docs/site/static/json/developer/iota-101/transactions/sign-and-send-transactions.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/sign-and-send-txn.json rename to docs/site/static/json/developer/iota-101/transactions/sign-and-send-transactions.json diff --git a/docs/site/static/json/developer/iota-101/transactions/sponsored-transactions.json b/docs/site/static/json/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/sponsored-transactions.json rename to docs/site/static/json/developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.json diff --git a/docs/site/static/json/developer/iota-101/transactions/sponsor-txn.json b/docs/site/static/json/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.json similarity index 100% rename from docs/site/static/json/developer/iota-101/transactions/sponsor-txn.json rename to docs/site/static/json/developer/iota-101/transactions/sponsored-transactions/use-sponsored-transactions.json diff --git a/dprint.json b/dprint.json index 17a5a9f5404..efec70de71e 100644 --- a/dprint.json +++ b/dprint.json @@ -20,7 +20,6 @@ "crates/iota-light-client/example_config/20873329.yaml", "**/pnpm-lock.yaml", "external-crates/move/crates/move-stdlib/**/docs/**/*.md", - "external-crates/move/move-execution/v0/crates/move-stdlib/**/docs/**/*.md", "nre/helm/graphql/templates/**/*.yaml", "nre/helm/indexer/templates/**/*.yaml" ], diff --git a/examples/custom-indexer/rust/README.md b/examples/custom-indexer/rust/README.md index 52804260008..ff88fc603b5 100644 --- a/examples/custom-indexer/rust/README.md +++ b/examples/custom-indexer/rust/README.md @@ -2,7 +2,7 @@ This is a complimentary example to the Iota Custom Indexer documentation. It demonstrates how to create a custom indexer for the Iota search engine. -See the [Iota Custom Indexer documentation](https://wiki.iota.org/guides/developer/advanced/custom-indexer) for more information. +See the [Iota Custom Indexer documentation](https://docs.iota.org/developer/advanced/custom-indexer) for more information. ## Prerequisites diff --git a/examples/tic-tac-toe/ui/README.md b/examples/tic-tac-toe/ui/README.md index 2a3dc86c2a4..cad40e908a2 100644 --- a/examples/tic-tac-toe/ui/README.md +++ b/examples/tic-tac-toe/ui/README.md @@ -22,7 +22,7 @@ Client dApp using the following tools: - [Vite](https://vitejs.dev/) for build tooling - [Radix UI](https://www.radix-ui.com/) for pre-built UI components - [ESLint](https://eslint.org/) -- [`@iota/dapp-kit`](https://wiki.iota.org/references/ts-sdk/dapp-kit) for connecting to +- [`@iota/dapp-kit`](https://docs.iota.org/references/ts-sdk/dapp-kit) for connecting to wallets and loading data - [pnpm](https://pnpm.io/) for package management diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index 5446c9a86a3..812fbf776bc 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -361,20 +361,6 @@ dependencies = [ "petgraph", ] -[[package]] -name = "bytecode-verifier-tests-v0" -version = "0.1.0" -dependencies = [ - "hex", - "move-abstract-interpreter", - "move-binary-format", - "move-bytecode-verifier-meter", - "move-bytecode-verifier-v0", - "move-core-types", - "move-vm-config", - "petgraph", -] - [[package]] name = "bytecode-verifier-transactional-tests" version = "0.1.0" @@ -1468,15 +1454,6 @@ dependencies = [ "move-bytecode-verifier-meter", ] -[[package]] -name = "move-abstract-interpreter-v0" -version = "0.1.0" -dependencies = [ - "itertools", - "move-binary-format", - "move-bytecode-verifier-meter", -] - [[package]] name = "move-abstract-stack" version = "0.0.1" @@ -1581,20 +1558,6 @@ dependencies = [ "move-vm-config", ] -[[package]] -name = "move-bytecode-verifier-v0" -version = "0.1.0" -dependencies = [ - "move-abstract-interpreter", - "move-abstract-stack", - "move-binary-format", - "move-borrow-graph", - "move-bytecode-verifier-meter", - "move-core-types", - "move-vm-config", - "petgraph", -] - [[package]] name = "move-bytecode-viewer" version = "0.1.0" @@ -2011,20 +1974,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "move-stdlib-natives-v0" -version = "0.1.1" -dependencies = [ - "hex", - "move-binary-format", - "move-core-types", - "move-vm-runtime-v0", - "move-vm-types", - "sha2", - "sha3", - "smallvec", -] - [[package]] name = "move-symbol-pool" version = "0.1.0" @@ -2158,27 +2107,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "move-vm-runtime-v0" -version = "0.1.0" -dependencies = [ - "anyhow", - "better_any", - "fail", - "hex", - "move-binary-format", - "move-bytecode-verifier-v0", - "move-core-types", - "move-vm-config", - "move-vm-profiler", - "move-vm-types", - "once_cell", - "parking_lot 0.11.2", - "proptest", - "smallvec", - "tracing", -] - [[package]] name = "move-vm-test-utils" version = "0.1.0" diff --git a/external-crates/move/Cargo.toml b/external-crates/move/Cargo.toml index 2e927598fc5..553f221bceb 100644 --- a/external-crates/move/Cargo.toml +++ b/external-crates/move/Cargo.toml @@ -3,11 +3,6 @@ resolver = "2" members = [ "crates/*", - "move-execution/v0/crates/bytecode-verifier-tests", - "move-execution/v0/crates/move-abstract-interpreter", - "move-execution/v0/crates/move-bytecode-verifier", - "move-execution/v0/crates/move-stdlib-natives", - "move-execution/v0/crates/move-vm-runtime", # "move-execution/$CUT/crates/bytecode-verifier-tests", # "move-execution/$CUT/crates/move-abstract-interpreter", # "move-execution/$CUT/crates/move-bytecode-verifier", diff --git a/external-crates/move/crates/move-analyzer/editors/code/README.md b/external-crates/move/crates/move-analyzer/editors/code/README.md index ca25dc39830..0c4b6749a7d 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/README.md +++ b/external-crates/move/crates/move-analyzer/editors/code/README.md @@ -22,7 +22,7 @@ the pre-built move-analyzer binary is not available for your platform or if you binary stored in a different location. If you want to build and test Move code using the extension, you must install the `iota` binary on -your machine - see [here](https://wiki.iota.org/guides/developer/getting-started/iota-install) for +your machine - see [here](https://docs.iota.org/developer/getting-started/install-iota) for instructions. The extension assumes that the `iota` binary is in your system path, but you can set its custom location location using VSCode's settings (`⌘` + `,` on macOS, or use the menu item _Code > Preferences > Settings_). Search for the `move.iota.path` user setting, set it to the new location of @@ -41,7 +41,7 @@ This can be done in two steps: 1. Install the move-analyzer installation prerequisites for your platform. They are the same as prerequisites for Iota installation - for Linux, macOS and Windows these prerequisites and their installation instructions can be found - [here](https://wiki.iota.org/guides/developer/getting-started/iota-install#additional-prerequisites-by-operating-system) + [here](https://docs.iota.org/developer/getting-started/install-iota#additional-prerequisites-by-operating-system) 2. Invoke `cargo install --git https://github.com/iotaledger/iota iota-move-lsp` to install the `move-analyzer` language server in your Cargo binary directory, which is typically located in the `~/.cargo/bin` (macOS/Linux) or `C:\Users\USER\.cargo\bin` (Windows) directory. diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/Cargo.toml b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/Cargo.toml deleted file mode 100644 index e6484d36ef7..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bytecode-verifier-tests-v0" -version = "0.1.0" -authors = ["IOTA Foundation "] -edition = "2021" -license = "Apache-2.0" -publish = false -description = "Move bytecode verifier tests" - -[dev-dependencies] -hex.workspace = true -petgraph.workspace = true - -move-abstract-interpreter.workspace = true -move-binary-format = { workspace = true, features = ["fuzzing"] } -# referred to via path for execution versioning -move-bytecode-verifier = { path = "../move-bytecode-verifier", package = "move-bytecode-verifier-v0" } -move-bytecode-verifier-meter.workspace = true -move-core-types.workspace = true -move-vm-config.workspace = true - -[features] -fuzzing = ["move-binary-format/fuzzing"] diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/METER_TESTING.md b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/METER_TESTING.md deleted file mode 100644 index 2f1865f6ed6..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/METER_TESTING.md +++ /dev/null @@ -1,5 +0,0 @@ -This testsuite can be run in a specific way to print the time until a 'complex' program is detected or accepted. Call as in: - -``` -cargo test --release -- --nocapture 1>/dev/null -``` diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/lib.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/lib.rs deleted file mode 100644 index 0dc645aec48..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -#[cfg(test)] -pub mod support; - -#[cfg(test)] -pub mod unit_tests; diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/support/mod.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/support/mod.rs deleted file mode 100644 index 266dddf48a2..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/support/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::{ - file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, IdentifierIndex, - ModuleHandleIndex, SignatureIndex, - }, - CompiledModule, -}; - -/// Create a dummy module to wrap the bytecode program in local@code -pub fn dummy_procedure_module(code: Vec) -> CompiledModule { - let mut module = empty_module(); - let code_unit = CodeUnit { - code, - ..Default::default() - }; - let fun_def = FunctionDefinition { - code: Some(code_unit), - ..Default::default() - }; - - let fun_handle = FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }; - - module.function_handles.push(fun_handle); - module.function_defs.push(fun_def); - module -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/binary_samples.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/binary_samples.rs deleted file mode 100644 index 624004452a2..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/binary_samples.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Tests in here are based on binary representation of modules taken from -//! production. Those tests may fail over time if the representation becomes out -//! of date, then they can be removed. Right now the serve to calibrate the -//! metering working as expected. Those tests represent cases which we want to -//! continue to succeed. - -use move_binary_format::{errors::VMResult, CompiledModule}; -use move_bytecode_verifier::verifier; -use move_bytecode_verifier_meter::bound::BoundMeter; - -use crate::unit_tests::production_config; - -#[allow(unused)] -fn run_binary_test(name: &str, bytes: &str) -> VMResult<()> { - let bytes = hex::decode(bytes).expect("invalid hex string"); - let m = CompiledModule::deserialize_with_defaults(&bytes).expect("invalid module"); - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - verifier::verify_module_with_config_for_test(name, &verifier_config, &m, &mut meter) -} - -#[test] -fn sample_price_oracle() { - let code = "a11ceb0b050000000c01001602162003368a0104c0011405d401a80607fc07f90308f50b6006d50c9b3210f03e770ae73f170cfe3f951e0d935e060000010101020103010401050006020702080209020a000b0800000c0800080d070004170402030100010318070009190700071c0700000e000100000f0201010000100201010000110302000012030200001304020000140506000015050600080f080900071d0a0100081e080900071f0a0b0007200a010006210c01000122020f01000123020c01000424110b020300042511120203000a261314000527020f0100022803180004290219020304092a1a1b00032b1a0f00042c1c02020302020e0e0e0f0e10101110130e1016111615101810020608020201030001060c02060c0304030103010203010908060303080603010301030106080201080601060806010101020502060800080508040802010900010804020804080502060b0302090009010900010609010208050301080202080406080102080403ae010a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a02080508000708000a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050a0208050105010b030209000901010a0201080503070b0302090009010900090193010508000708000a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a020a02080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805080508050805010708000b03030101010301030103010b01030101010301030103010c70726963655f6f7261636c6504636f696e067369676e657206737472696e67057461626c6509747970655f696e666f046d617468036936340570726963651070726963655f6964656e74696669657204707974681250726963654964656e7469666965724d61700e54657374507269636553746f72650550726963650c666f726d61745f7072696365096765745f7072696365126765745f70726963655f666f725f746573740a696e697469616c697a6512696e697469616c697a655f746573746e6574127365745f7468726573686f6c645f73656373107369676e65645f7536345f6d696e75730f7369676e65645f7536345f706c757304696e666f055461626c6506537472696e670f50726963654964656e7469666965720e7468726573686f6c645f736563730b70726963655f7461626c6503493634196765745f6d61676e69747564655f69665f706f736974697665086765745f6578706f0f6765745f69735f6e65676174697665196765745f6d61676e69747564655f69665f6e6567617469766506706f775f31300673796d626f6c08646563696d616c7308636f6e7461696e7306626f72726f77176765745f70726963655f6e6f5f6f6c6465725f7468616e09747970655f6e616d650a616464726573735f6f66036e65770d66726f6d5f627974655f76656304757466380675707365727414812adfd8ba2f589c0fe48c5170fa287cd36050dd487d17e2f4a765101f70ed00000000000000000000000000000000000000000000000000000000000000017e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b3870308700000000000000003086d0000000000000003086e0000000000000003081000000000000000052014812adfd8ba2f589c0fe48c5170fa287cd36050dd487d17e2f4a765101f70ed0a0221202b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e474450a020504414156450a022120bd640cddb72063e2ede34c6a0baf6699759b9837fcb06aa0e2fbcecb9b65fde70a02040341434d0a0221202a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d0a0204034144410a022120fa17ceaf30d19ba51112fdcc750cc83454776f47fb0112e4af07f15f4bb1ebc00a020504414c474f0a0221206d0af467543fc7daedf7abed96423877560c8d03725f3e5c87516774982a679c0a020403414e430a02212015add95022ae13563a11992e727c91bdb6b55bc183d9d747436c80a483d8c8640a0204034150450a02212003ae4db29ed4ae33d323568895aa00337e658e348b37509f5372ae51f0af00d50a0204034150540a022120b881c6dad5dd3dc9a83222f8032fb439859288119afc742d43adc305cef151cc0a0204034153520a022120681e0eb7acf9a2a3384927684d932560fb6f67c6beb21baa0f110e993b2653860a02060541544c41530a0221208ff1200345393bb25be4f4eeb2d97234e91f7e6213f3745a694b1436e700f2710a02040341544d0a022120b00b60f88b03a6a625a8d1c048c3f66653edf217439983d037e7222c4e6128190a02050441544f4d0a0221204cbd623d7aa47003fff1cd75c3f96cb24a660014b697d91cfb7adcd204b952020a020504415553540a02212093da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb70a020504415641580a022120b7e3904c08ddd9c0c10c6d207d390fd19e87eb6aab96304f571ed94caebdefa00a0204034158530a0221209d23a47f843f5c9284832ae6e76e4aa067dc6072a58f151d39a65a4cc792ef9f0a0204034241520a0221203dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a30a0204034243480a0221207f981f906d7cfe93f618804f1de89e0199ead306edc022d3230b3e8305f391b00a020504424554480a0221202f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f0a020403424e420a0221201ce9069708fb49e2f1b062fa4f1be0bb151475ca506939d6d8c14386d49f43dc0a02040342525a0a022120e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b430a0204034254430a022120097d687437374051c75160d648800f021086bc8edf469f11284491fda81923150a0204034254540a0221205bc91f13e412c07599167bae86f07543f076a638962b8d6017ec19dab4a828140a020504425553440a0221202dd14c7c38aa7066c7a508aac299ebcde5165b07d5d9f2d94dfbfe41f0bc5f2e0a0204034339380a0221202356af9529a1064d41e32d617e2ce1dca5733afa901daba9e2b68dee5d53ecf90a02050443414b450a02212015ecddd26d49e1a8f1de9376ebebc03916ede873447c1255d2d5891b92ce57170a02060543424554480a022120bd4dbcbfd90e6bc6c583e07ffcb5cb6d09a0c7b1221805211ace08c8378596270a0204034348520a022120e799f456b358a2534aa1b45141d454ac04b444ed23b1440b778549bb758f2b5c0a02040343485a0a0221209c479b12a2b2c1051715d4d462dd7a6abbb6dccabf3af31a53f6130a1cd88efc0a020504434954590a0221208517b35589b5f1f6ad626da2379ff592d5101b63aa661d75b7a883fdbda023f00a020504434f50450a0221204e53c6ef1f2f9952facdcf64551edb6d2a550985484ccce6a0477cae4c1bca3e0a020403434f570a0221208f218655050a1476b780185e89f19d2b1e1f49e9bd629efad6ac547a946bf6ab0a020504435553440a022120b0948a5e5313200c632b51bb5ca32f6de0d36e9950a942d19751e833f70dabfd0a0204034441490a022120dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c0a020504444f47450a022120ca3eed9b267293f6595901c734c7525ce8ef49adafe8284606ceb307afa2ca5b0a020403444f540a022120ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace0a0204034554480a022120c80657b7f6f3eac27218d09d5a4e54e47b25768d9f5e10ac15fe2cf9008814000a020504464944410a0221202fb245b9a84554a0f15aa123cbb5f64cd263b59e9a87d80148cbffab50c69f300a020504464c4f570a0221205c6c0d2386e3352356c3ab84434fafb5ea067ac2678a38a338c4a69ddc4bdb0c0a02040346544d0a0221206c75e52531ec5fd3ef253f6062956a8508a2f03fa0a209fb7fbc51efd9d35f880a0204034654540a022120301377b122716cee1a498e7930a1836c0b1db84667cc78bbbcbad6c330ea6afb0a02040347414c0a0221200781209c28fda797616212b7f94d77af3a01f3e94a5d421760aef020cf2bcb510a02050447414c410a022120baa284eaf23edf975b371ba2818772f93dbae72836bbdea28b07d40f3cf8b4850a020403474d540a0221206034b1f68b9363dff2cf9d53b1a88fb4d0929c65f34d532db53738853efc00ad0a020504474f46580a02212095609d32c98a467a72ac419f2e64bb2b8dbd5b00b74f3a0fd72f42343af1743d0a0205044858524f0a0221207a5bc1d2b56ad029048cd63964b3ad2776eadf812edc1a43a31406cb54bff5920a020403494e4a0a022120a4702f0f5818258783a1e47f453cb20b0fbec32ca67260e1d19dfcdd6a4d0ebb0a020605494e5445520a02212081a21b01c15b8d01f6cdfed65e00987cc4c901858c821b2089344987de3102e90a0204034a45540a022120ee42016c303126bd9263724e00f83a8114e84518c6e8ffc9738c001cc301daff0a0204034a53540a022120abe4f2b264560a397f38eec024369356e5c1ea4f7aab94729369f144b3d977790a0204034a55560a022120d1d95644ffc11ca502f21e067a7814144c56b37018515ced4335a886a827a3050a0206054c415a494f0a0221206e3f3fa8253588df9326580180233eb791e03b443a3ba7a1d892e73874e19a540a0204034c54430a022120e6ccd3f878cf338e6732bf59f60943e8ca2c28402fc4d9c258503b2edbe74a310a0205044c554e410a0221204456d442a152fd1f972b18459263ef467d3c29fb9d667e30c463b086691fbc790a0205044c554e430a0221205de33a9112c2b700b8d30b8a3402c103578ccfa2765696471cc672bd5cf6ac520a0206054d415449430a0221201888f463c27997174f97d2a36af29bf4648b61a5f69e67c45505a80f826bb7850a0205044d424f580a02212027d108eb764c912f49d3453a21dd95516619b1c45d0b607ee58a137ac8a6f32d0a0205044d45414e0a022120dfaeafde7f8ad3cb9ddbcc85ed7ae7e53910e7ab377fd75921920f97c80a8edc0a0204034d45520a0221200b46c1c04e9c914037cc4e0561a7e6787f6db0b89b7b65281f0f6fea1ce45a740a0204034d49520a0221205b70af49d639eefe11f20df47a0c0760123291bb5bc55053faf797d1ff9059830a0205044d4e474f0a022120c2289a6a43d2ce91c6f55caec370f4acc38a2ed477f58813334c6d03749ff2a40a0205044d534f4c0a022120c415de8d2eba7db216527dff4b60e8f3a5311c740dadb233e13e12547e2267500a0205044e4541520a02212005934526b94a9fbe4c4ce0c3792213032f086ee4bf58f2168a7085361af9bdc10a0203024f470a022120c572690504b42b57a3f7aed6bd4aae08cbeeebdadcf130646a692fe73ec1e0090a0204034f4e450a02212037505261e557e251290b8c8899453064e8d760ed5c65a779726f2490980da74c0a0205044f5243410a0221200afa3199e0899270a74ddcf5cc960d3c6c4414b4ca71024af1a62786dd24f52a0a020504504f52540a02212088e2d5cbd2474766abffb2a67a58755a2cc19beb3b309e1ded1e357253aa36230a020605504f52544f0a0221203d253019d38099c0fe918291bd08c9b887f4306a44d7d472c8031529141f275a0a0204035053470a022120fd0690232b0fae5efdc402c1b9aac74176383ff7daf87d021554bda24a38e0ec0a020504524143410a02212091568baa8beb53db23eb3fb7f22c6e8bd303d103919e19733f2bb642d3e7987a0a0204035241590a0221206ed3c7c4427ae2f91707495fc5a891b30795d93dbb3931782ddd77a5d8cb6db70a0204035342520a0221201021a42d623ab4fe0bf8c47fd21cc10636e39e07f91e9b2478551e137d512aaa0a02070653434e534f4c0a022120f8d030e4ef460b91ad23eabbbb27aec463e3c30ecc8d5c4b71e92f54a36ccdbd0a020504534c4e440a0221209fb0bd29fe51481b61df41e650346cc374b13c2bab2e3610364cd834a592025a0a020403534e590a022120ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d0a020403534f4c0a02212023245bb74254e65a98cc3ff4a37443d79f527e44e449750ad304538b006f21bc0a02040353524d0a022120a1a6465f4c2ebf244c31d80bc95c27345a3424e428c2def33eced9e90d3f701b0a0206055354534f4c0a0221204457960559b812558bb0f8cb7912f8bcb843eb801a3133ef45be998630ff8c050a020403544c4d0a022120433faaa801ecdb6618e3897177a118b273a8e18cc3ff545aadfc207d58d028f70a020504545553440a022120eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a0a020504555344430a0221202b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b0a020504555344540a022120ef94acc2fb09eb976c6eb3000bab898cab891d5b800702cd1dc88e61d7c3c5e60a020504555354430a0221207507a4629ad0143550666bce2e7cae0b961a0f624f821feaab642fe1be632f5c0a0204035641490a022120b216f7ca372b318985903866e0b6dc44a14564828c49f36d0d363805aa76335c0a02040357494e0a022120b82449fd728133488d2d41131cffe763f9c1693b73c544d9ef6aaa371060dd250a020403574f4f0a022120831624f51c7bd4499fe5e0f16dfa2fd22584ae4bdc496bbbbe9ba831b2d9bce90a0204035856530a02212026852e2d0696e25e6adaad2d7ca3a1f2f15aab68d317ace14d41b4128a7e780f0a0204035a42430a022120d6b3bc030a8bbb7dd9de46fb564c34bb7f860dead8985eb16a49cdc62f8ab3a50a02212073dc009953c83c944690037ea477df627657f45c14f16ad3a61089c5a3f9f4f20a02212008f781a893bc9340140c5f89c8a96f438bcfae4d1474cc0f688e3a52892c73180a0221204204a764d9e0ca8db0fd43b62728265bf82671ce8a8b4bfedba13672be12de620a022120cb1743d0e3e3eace7e84b8230dc082829813e3ab04e91b503c08e9a441c0ea8b0a02212044a93dddd8effa54ea51076c4e851b6cbbfd938e82eb90197de38fe8876bb66e0a022120c11efd88a9b2c96bc84bccd6ce4aa05bebad7aa760dbe5f2f00ef90c4abd50cc0a02212061226d39beea19d334f17c2febce27e12646d84675924ebb02b9cdaea68727e30a022120d7566a3ba7f7286ed54f4ae7e983f4420ae0b1e0f3892e11f9c4ab107bbad7b90a02212030029479598797290e3638a1712c29bde2367d0eca794f778b25b5a472f192de0a022120fc309467defa4b198c6b5bd59c08db4b9dfb27ddbcc32f31560f217b4ff8fc2b0a022120ecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0a022120457064c555edae5b6b89267e992be4c58be5375f0e9ca9749391c7094f7d90330a022120f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b0a022120eb3b5720301b36fa074260e6e27e2cfff3a7c6bbb47564eb218a9a7f2a8c01930a02212006c532524fabd49cc142815502d785e4f34ec3bd035480efb770568d423f46c60a022120c098773aa18ebb7e84d5ee7ea7d97105ab2015a329f670012398cd2c8f5cc6ca0a0221200d10b9ccdc9af1d65cd4bafabf4e34db01c0ce83abba190230eaf26d7592cd210a022120970541e943e11d1bf1d4d763bc8e70995f613dc682f727045a866312312a63760a02212013ac8c71e4e1c61dece57bea76163d393bf75cd0db8e52981e1564fa592698cb0a022120b59d65c2e10af17c0d6f7d10be8d64c0c13dd15d957c4c39763b4cc55e5919370a02212087a67534df591d2dd5ec577ab3c75668a8e3d35e92e27bf29d9e2e52df8de4120a02212031775e1d6897129e8a84eeba975778fb50015b88039e9bc140bbd839694ac0ae0a02212036032e522b810babd8e3148e9f0d588af9e95e93b97ffb58566b837fdbd31f7f0a022120ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a60a0221206660d7629213995af8aa169ad6a1cf1a725765d3741b7e7a27825f884a701b960a022120aa67a6594d0e1578faa3bba80bec5b31e461b945e4fbab59eeab38ece09335fb0a0221209b7bfd7654cbb80099d5edc0a29159afc9e9b4636c811cf8c3b95bd11dd8e3dd0a0221200184a60a47ab7c23fcb828d727b05748ef52433fb5ae96f775cc98c9049d8c2c0a0221206354d685c247f1efc36f967617beffa2ebd5a95d8ffe17de4498505c923067b80a02212021adabf73ad61903c1872fa220f84d3b1b9990cec2047f8317e2a67efcd1764e0a022120c999fe8a75bf9528931deb832e2934dd1ac138757d7f2c1398675b6f521d556f0a02212087fd5a8d0bf026239f98b53311d2cec2ee171e853f545e1ad742e092a97ef8600a02212051ae9b785e6aeb288955720faef15f63ee3ae21af38114ade8b3fc3d3831b5ad0a0221202d9315a88f3019f8efa88dfe9c0f0843712da0bac814461e27733f6b83eb51b30a0221202245aec1db780b8dc9f710173256996164f0a65f25a975453e4202a54371d2ea0a022120077e345652cb98edba89815a63a0ede724eba12d7a10c8498e0e762d581aca6b0a022120997e0bf451cb36b4aea096e6b5c254d700922211dd933d9d17c467f0d6f343210a022120677dbbf4f68b5cb996a40dfae338b87d5efb2e12a9b2686d1ca16d69b3d7f2040a0221206de025a4cf28124f8ea6cb8085f860096dbc36d9c40002e221fc449337e065b20a022120d2c2c1f2bba8e0964f9589e060c2ee97f5e19057267ac3284caef3bd50bd2cb50a02212070bf2974182b6ebf4eac64977170074315e7a07d7b58a963758060924ee9a1920a0221209f2acb902ee5c3a8e41c643164a498ed3089c99df3477189babd3682801340750a02212052800482aa2666c0d2621f8f0a8c19ee6e5e5039a65524e268315501b3355fbb0a0221202f317bea04ce89adb8acc85a1a68d20d6e89976d5e86306570a0bb3107e4916a0a022120b5366e5ded52c3db5d81635a624a39d81eb8c639427fc79c539e5c91e17c84940a0221207f57ca775216655022daa88e41c380529211cde01a1517735dbcf30e4a02bdaa0a02212027e867f0f4f61076456d1a73b14c7edc1cf5cef4f4d6193a33424288f11bd0f40a0221209b245faf19d2f3306b58821ef60b194d7b67eb5250635059d8eaae4a36337b280a02212085dafbfa99c733fdd33649551666958ca8f82fd0d849746e093b770caab249d20a0221206b86b78584e182c61729267d73593f48b6a929f3218234769281513cbe5e87ee0a0204035041490a0221201e761bb4e6b1e4d7899b618d389516ddbdfbdb858018b71c45cb6ade57c96f620a0221207730809b4a4656325b7fbc8157fd803c3da170727c541afc4db37773246258b10a022120cb9372930932694b8a74dd5599c0f9e236575f434faf05141d95803a91f044ed0a022120341d9502b1e6bc445036fd76f57bc824c09254a754289878b52a40eb067d2a0b0a022120f9917668f24038a383e51c0b019f5d9645d6785c6bf9a0d48483edf82ba5caec0a022120dd51a67a1b4d0702abf5b5385631b7d3e586993eadacf2e0d900c0a81edcffdd0a022120b5d2cee0b86bb0571db898ae7b9f288c5e272fa486d41a5bf375b52a0249d94c0a022120fe650f0367d4a7ef9815a593ea15d36593f0643aaaf0149bb04be67ab851decd0a02212078ec25615d53d2486db101e829f77615c4408cbbd543088714b9f267da44591a0a022120b718e659619a031b41c6ea9122f146859a82d4a7edc17c55b1e5188e8b4c1d1c0a020504535445500a02212013f7925c9186b8cca50dde4bbf2d4fa9705052cceb6f2fc79df827e2b37d6f3b0a0221200adbf6463e27f60f171bcc498703a4282d1954ec3a85e1f6698b48ecde18c00e0a0221201bd1c59cd425aebf8a931c1b2a902b9a11e7d18dc68021e4a35a5cf24220a6c70a02212041f3625971ca2ed2263e78573fe5ce23e13d2558ed3f2e47ab0f84fb9e7ae7220a0221201fc18861232290221461220bd4e2acd1dcdfbc89c84092c93c18bdc7756c15880a022120026d1f1cf9f1c0ee92eb55696d3bd2393075b611c4f468ae5b967175edc4c25c0a022120eca4f9c5ce7cbef8a349fa9f6c12b23ac45b9ad91c4d1808c6a2dda841f95a020a022120834d3f05278b6f5c963670f437a2267c160914707328f888418902106bcd13d70a022120bf517e0f7ccfc307f0b2fa93b99a737641933989af6af769c928725989c21e660a0221206ff709bf1f0fdb46d991ae6839163df1e050d143429d737fc650b6b2143478080a02212064abe8a459058c60d55427f62be3dc4942c0ba93d1eb5cb85117b70b8e4651ad126170746f733a3a6d657461646174615f763063036d000000000000001a4552525f4e4f545f454e4f5547485f5045524d495353494f4e53006e00000000000000134552525f544f4b454e5f4e4f545f464f554e44007000000000000000174552525f414c52454144595f494e495449414c495a454400000202160b0302080408051a030102011b0b03020804030000000007440a0011080c020e0211090c080b00110a0c050600000000000000000c0a090c090b0a0b0907030911070c090c0a0e05110b0c070a07031a051e0e05110c0c0305210e0511090c030b030c060b0a0b090b060b0711070c090c0a0b0a0b090b01340911060c090c0a0b090335053c0b080b0a33110d1a0c0405420b080b0a33110d180c040b04020101000200010d26070429010304050638000238010c0338020c0007042b000c010a0110000a03380303160b01010702270a0110000b033804140c020b020b0110011411120c040e040b001100020200000101151438050c0007042b010c010a0110020a003806030e0b01010702270b0110020b0038071402030104010017a4060a00111407042103090b00010701270a00111429002003120b00010700273808063c0000000000000012000c6b0a000b6b2d000b0011142a000c6c070511160c020a6c0f00070611170b023809070711160c040a6c0f00070811170b043809070911160c060a6c0f00070a11170b063809070b11160c080a6c0f00070c11170b083809070d11160c0a0a6c0f00070e11170b0a3809070f11160c0c0a6c0f00071011170b0c3809071111160c0e0a6c0f00071211170b0e3809071311160c100a6c0f00071411170b103809071511160c120a6c0f00071611170b123809071711160c140a6c0f00071811170b143809071911160c160a6c0f00071a11170b163809071b11160c180a6c0f00071c11170b183809071d11160c1a0a6c0f00071e11170b1a3809071f11160c1c0a6c0f00072011170b1c3809072111160c1e0a6c0f00072211170b1e3809072311160c200a6c0f00072411170b203809072511160c220a6c0f00072611170b223809072711160c240a6c0f00072811170b243809072911160c260a6c0f00072a11170b263809072b11160c280a6c0f00072c11170b283809072d11160c2a0a6c0f00072e11170b2a3809072f11160c2c0a6c0f00073011170b2c3809073111160c2e0a6c0f00073211170b2e3809073311160c300a6c0f00073411170b303809073511160c320a6c0f00073611170b323809073711160c340a6c0f00073811170b343809073911160c360a6c0f00073a11170b363809073b11160c380a6c0f00073c11170b383809073d11160c3a0a6c0f00073e11170b3a3809073f11160c3c0a6c0f00074011170b3c3809074111160c3e0a6c0f00074211170b3e3809074311160c400a6c0f00074411170b403809074511160c420a6c0f00074611170b423809074711160c440a6c0f00074811170b443809074911160c460a6c0f00074a11170b463809074b11160c480a6c0f00074c11170b483809074d11160c4a0a6c0f00074e11170b4a3809074f11160c4c0a6c0f00075011170b4c3809075111160c4e0a6c0f00075211170b4e3809075311160c500a6c0f00075411170b503809075511160c520a6c0f00075611170b523809075711160c540a6c0f00075811170b543809075911160c560a6c0f00075a11170b563809075b11160c580a6c0f00075c11170b583809075d11160c5a0a6c0f00075e11170b5a3809075f11160c5c0a6c0f00076011170b5c3809076111160c5e0a6c0f00076211170b5e3809076311160c600a6c0f00076411170b603809076511160c620a6c0f00076611170b623809076711160c640a6c0f00076811170b643809076911160c660a6c0f00076a11170b663809076b11160c680a6c0f00076c11170b683809076d11160c6a0a6c0f00076e11170b6a3809076f11160c6e0a6c0f00077011170b6e3809077111160c700a6c0f00077211170b703809077311160c720a6c0f00077411170b723809077511160c740a6c0f00077611170b743809077711160c760a6c0f00077811170b763809077911160c780a6c0f00077a11170b783809077b11160c7a0a6c0f00077c11170b7a3809077d11160c7c0a6c0f00077e11170b7c3809077f11160c7e0a6c0f0007800111170b7e380907810111160c80010a6c0f0007820111170b8001380907830111160c82010a6c0f0007840111170b8201380907850111160c84010a6c0f0007860111170b8401380907870111160c86010a6c0f0007880111170b8601380907890111160c88010a6c0f00078a0111170b88013809078b0111160c8a010a6c0f00078c0111170b8a013809078d0111160c8c010a6c0f00078e0111170b8c013809078f0111160c8e010a6c0f0007900111170b8e01380907910111160c90010a6c0f0007920111170b9001380907930111160c92010a6c0f0007940111170b9201380907950111160c94010a6c0f0007960111170b9401380907970111160c96010a6c0f0007980111170b9601380907990111160c98010a6c0f00079a0111170b98013809079b0111160c9a010a6c0f00079c0111170b9a013809079d0111160c9c010a6c0f00079e0111170b9c013809079f0111160c9e010a6c0f0007a00111170b9e01380907a10111160ca0010a6c0f0007a20111170ba001380907a30111160ca2010a6c0f0007a40111170ba201380907a50111160ca4010a6c0f0007a60111170ba401380907a70111160ca6010a6c0f0007a80111170ba601380907a90111160ca8010a6c0f0007aa0111170ba801380907ab0111160caa010a6c0f0007ac0111170baa01380907ad0111160cac010a6c0f0007ae0111170bac01380907af0111160cae010b6c0f0007b00111170bae0138090204010401001da7050a0011140c010a01070421030b0b00010701270a00111429002003140b0001070027380806504600000000000012000c020b000b022d000b012a000c0307b10111160c4c0a030f00070611170b4c380907b20111160c6d0a030f00070a11170b6d380907b30111160c780a030f00070c11170b78380907b40111160c83010a030f00070e11170b8301380907b50111160c8e010a030f00071011170b8e01380907b60111160c520a030f00071211170b52380907b70111160c5d0a030f00071611170b5d380907b80111160c650a030f00071a11170b65380907b90111160c660a030f00071e11170b66380907ba0111160c670a030f00072411170b67380907bb0111160c680a030f00072611170b68380907bc0111160c690a030f00072811170b69380907bd0111160c6a0a030f00072a11170b6a380907be0111160c6b0a030f00072c11170b6b380907bf0111160c6c0a030f00072e11170b6c380907c00111160c6e0a030f00073011170b6e380907c10111160c6f0a030f00073211170b6f380907c20111160c700a030f00073811170b70380907c30111160c710a030f00073e11170b71380907c40111160c720a030f00074011170b72380907c50111160c730a030f00074211170b73380907c60111160c740a030f00074411170b74380907c70111160c750a030f00074611170b75380907c80111160c760a030f00074811170b76380907c90111160c770a030f00074a11170b77380907ca0111160c790a030f00074c11170b79380907cb0111160c7a0a030f00074e11170b7a380907cc0111160c7b0a030f00075011170b7b380907cd0111160c7c0a030f00075211170b7c380907ce0111160c7d0a030f00075411170b7d380907cf0111160c7e0a030f00075611170b7e380907d00111160c7f0a030f00075811170b7f380907d10111160c80010a030f00075a11170b8001380907d20111160c81010a030f00075c11170b8101380907d30111160c82010a030f00075e11170b8201380907d40111160c84010a030f00076211170b8401380907d50111160c85010a030f00076411170b8501380907d60111160c86010a030f00076a11170b8601380907d70111160c87010a030f00076c11170b8701380907d80111160c88010a030f00076e11170b8801380907d90111160c89010a030f00077011170b8901380907da0111160c8a010a030f00077211170b8a01380907db0111160c8b010a030f00077411170b8b01380907dc0111160c8c010a030f00077611170b8c01380907dd0111160c8d010a030f00077811170b8d01380907de0111160c8f010a030f00077a11170b8f01380907df0111160c90010a030f00077c11170b9001380907e00111160c91010a030f00077e11170b9101380907e10111160c92010a030f0007820111170b9201380907e20111160c93010a030f0007840111170b9301380907e30111160c4d0a030f0007e40111170b4d380907e50111160c4e0a030f0007860111170b4e380907e60111160c4f0a030f00078c0111170b4f380907e70111160c500a030f00078e0111170b50380907e80111160c510a030f0007900111170b51380907e90111160c530a030f0007920111170b53380907ea0111160c540a030f0007940111170b54380907eb0111160c550a030f0007960111170b55380907ec0111160c560a030f0007980111170b56380907ed0111160c570a030f00079a0111170b57380907ee0111160c580a030f0007ef0111170b58380907f00111160c590a030f00079c0111170b59380907f10111160c5a0a030f00079e0111170b5a380907f20111160c5b0a030f0007a00111170b5b380907f30111160c5c0a030f0007a20111170b5c380907f40111160c5e0a030f0007a40111170b5e380907f50111160c5f0a030f0007a60111170b5f380907f60111160c600a030f0007a80111170b60380907f70111160c610a030f0007aa0111170b61380907f80111160c620a030f0007ac0111170b62380907f90111160c630a030f0007ae0111170b63380907fa0111160c640b030f0007b00111170b6438090205010401001e120a00111407042103090b00010701270b0011142a000c020b010b020f011502060000001f5d0a010a031f0305051c0a020a0026030a05110b020b0017090c070c0405170b000b0217080c070c040b040b070c060c05055a0a01031f05230a03200c080525090c080b080328052f0b000b0216080c0e0c0d05560b01200b031f0335053c0b000b0216090c0c0c0b05520a000a0226034105480b000b0217090c0a0c09054e0b020b0017080c0a0c090b090b0a0c0c0c0b0b0b0b0c0c0e0c0d0b0d0b0e0c060c050b050b060207000000205e0a010a031f03050508080c0705140a0120030c05100a03200c040512090c040b040c070b070317051e0b000b02160b010c060c05055b0b01032105250b03200c080527090c080b08032a05410a000a0224032f05360b000b0217080c0a0c09053c0b020b0017090c0a0c090b090b0a0c0e0c0d05570a020a00240346054d0b020b0017080c0c0c0b05530b000b0217090c0c0c0b0b0b0b0c0c0e0c0d0b0d0b0e0c060c050b050b060200000001010000"; - let res = run_binary_test("sample_price_oracle", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_aptosd_swap() { - let code = "a11ceb0b050000000c0100160216300346ee0104b4022205d602960407ec06de0708ca0e40068a0f8b01109510f9010a8e12520ce012e80e0dc821160000010101020103010401050106010700080009000a000b0700000c0700000d0800000e0700000f07000a28040207000400042b0401060102380800083a0000063c070100000010000100001102010000120101000013030100001401010000150101000016020100001701040000180506000019010700001a020100001b020100001c010100001d000100001e020100001f02010000200001000021000100092f01090009300105000a310b0c02070604320e01010607330205000934010f0009350104000936010f000a37110f020704033900010100033b02010100093d0104000a3e1617020704063f180f01000640191a01000a411b1c02070409421d010005431f040005441f0400094501200009460104000947010f0009480901000a490125020704014a022601060a4b1128020704140a150d1a0a1b121c1315141e0a1f052005210a151e290a2a0d2a142a1e2b0a1b1303060c05030001060c02060c0301030105050303030303010a050c03030303030505070b0502050803070b0502050803070802070803070b0601080401020205080303070b050209000901090009010107090101080402070b060109000900010108070b05020508030501070b06010800030107080207080302060b05020900090109000108070108080108002d0b0901050b0901050b0901050c0b0901050b0901050b0901050b0901050b090105030303030303070b06010801070b06010801070b06010801070b0601080103070803070803070803070803070b0502050803070b0502050803070b0502050803070b0502050803070b090105070b090105070b090105070b09010503030b0901050b0901050b0901050b090105070802070802070802070802070802030301060b050209000901010b0901090001060b0901090001070b0901090001090002070b0502090009010900030709010b090109000b09010900020503010801020303010c04010103030a03030303030505070b0502050803070b0502050803070803070b0901050b090105070803070b0502050803070b0901050b0901050a050a0b0502050803060c030c0b060108040c0b060108000c0b060108010c010b050209000901010b060109000c0b0901050c070b0502050803050b090105070b06010801060803070b0502050803070b0901050b090105070802060c030609010b090109000b090109000d6170746f737061645f73776170076163636f756e740a6170746f735f636f696e04636f696e056576656e74066d6174683634066f7074696f6e067369676e65720d6170746f737061645f636f696e06636f6e6669670e6974657261626c655f7461626c65104269644170746f735061644576656e7417446973747269627574654170746f737061644576656e74114c61756e636850616452656769737472790f546f6b656e446973747269627574650e57686974654c6973744576656e740c61646457686974654c6973740c6173736572745f61646d696e136173736572745f6e6f5f656d657267656e63790b6269644170746f735061640d64697374726962757465416c6c0e64697374726962757465417470701064697374726962757465536561736f6e0f67657453776170546f74616c4269640c67657457686974654c6973740d67657457686974654c697374730a696e697469616c697a650f6c61756e6368506164536561736f6e09726566756e64416c6c0b726566756e644170746f730b7265736574536561736f6e0f77686974654c697374536561736f6e0d77697468647261774170746f731077697468647261774170746f73506164036361700362696408696e766573746f72106469737472696275746564546f6b656e06726566756e6409696e766573746f72730d4974657261626c655461626c6508746f74616c426964126269646170746f737061645f6576656e74730b4576656e7448616e646c6519646973747269627574656170746f737061645f6576656e74731077686974656c6973745f6576656e74730b64697374726962757465640c676574537761705374617465126765745265736f757263654164647265737317626f72726f775f6d75745f776974685f64656661756c740a656d69745f6576656e740a616464726573735f6f660b6973456d657267656e63791467657453776170436f6e6669674861726443617011697342797061737357686974654c69737408636f6e7461696e73094170746f73436f696e087472616e736665720c4170746f73506164436f696e087265676973746572064f7074696f6e1a67657453776170436f6e666967417074546f417074745261746508686561645f6b65790769735f736f6d6507657874726163740f626f72726f775f697465725f6d75740a6d696e7441747070546f036d696e036d6178116765745265736f757263655369676e65721467657453776170436f6e666967536f66744361701967657453776170436f6e666967456e61626c65526566756e640c736574537761705374617465036e6577106e65775f6576656e745f68616e646c650b626f72726f775f6974657241376d8231e6aedc29a19459e804b3770a250c3f367ac8d0f91e10842e8c906d0000000000000000000000000000000000000000000000000000000000000001030800e87648170000000308950100000000000003089a0100000000000003089b010000000000000308930100000000000003089801000000000000030896010000000000000308970100000000000003089901000000000000020104020105020101020103020102052041376d8231e6aedc29a19459e804b3770a250c3f367ac8d0f91e10842e8c906d126170746f733a3a6d657461646174615f7630e4010893010000000000000f4552525f5045524d495353494f4e530095010000000000000d4552525f454d455247454e4359009601000000000000104552525f534541534f4e5f454e444544009701000000000000144552525f534541534f4e5f4e4f545f5245534554009801000000000000114552525f534541534f4e5f414354495645009901000000000000104552525f534541534f4e5f5354415445009a01000000000000134552525f484152444341505f52454143484544009b01000000000000144552525f4e4f545f494e5f57484954454c4953540b6572726f7220636f64657300020322032303240501020522032303250326032405020205270b050205080329032a0b060108002c0b060108012d0b06010804030206220323032e03250326032405040206220323032e032503260324050001000102083c0b0011011112070d21030807082711132a020c0c0a0c0f000c0b0b0c0f010c0e0b0b0c0a0a010c090b010c080b0a0b090600000000000000000600000000000000000600000000000000000600000000000000000600000000000000000b08120338000c0d0b020a0d0f02150b0e0a0d1002140a0d1003140a0d1004140a0d1005140a0d1005140b0d10061412043801020100000001080b001116070e210307070427020200000001061117200305070127020301000102106111021112070c2103090b000107082711180c0611132a020c0811190c040a080f000a0011160c030c020b022e0b0338020c070b040b071e03250b00010b08010703270a081007140b062503310b00010b08010702270a081007140a01160a080f07150a080f080c050a0011130a0138030a0038040b080f000a00111607000600000000000000000600000000000000000600000000000000000600000000000000000b001116120338000c090b010a090f03150b050a091002140a091003140b091006141200380502040000010215a00311180c13111d0c2b11132a020c260b261007140c2c0b2c0a13250310055c11132a020c270a270f000c180b270f090c0f0a182e38060c010d010c1c0a1c2e3807032405550a180b1c380838090c22010c140a141003140a140f0a150a14100a140a2b180a140f04150a141006140a1410041411220a0f0a141002140a141003140a141004140a141005140b141006141201380a0d220c1c051f0b1c010b18010b0f01059f030b130c0a11132a020c2a0a2a0f000c1b0b2a0f090c120a1b2e38060c040d040c1f0a1f2e3807037205c3010a1b0b1f380838090c23010c170a171002140c0d0a171003140c0b0b0d0b0b11230a0a11230c090a0906000000000000000024038c0105b4010a090a170f0a150a090a2b180a170f04150b0a0b09170c0a0a171006140a1710041411220a120a171002140a171003140a171004140a171005140b171006141201380a05b6010b17010a0a0600000000000000002503bb0105c0010b1b010b120105c3010d230c1f056d0a0a0600000000000000002403c80105b30211132a020c280a280f000c190b280f090c100a192e38060c000d000c1d0a1d2e380703dc0105b3020a190b1d380838090c24010c150a151003140a15100a141706000000000000000011240a0a11230c200a200600000000000000002403f40105a4020a15100a140a20160a150f0a150a200a2b180c210a151004140a21160a150f04150b0a0b20170c0a0a151006140b2111220a100a151002140a151003140a151004140a151005140b151006141201380a05a6020b15010a0a0600000000000000002503ab0205b0020b19010b100105b3020d240c1d05d7010b0a0600000000000000002503b90206a1860100000000002711132a020c290a290f000c1a0b290f090c110a1a2e38060c020d020c1e0a1e2e380703cd020599030a1a0b1e380838090c25010c160a161003140c0c0a16100a140c0e0a0c0a0e2603e8020b1a010b16010b1101061127000000000000270b0c0b0e1706000000000000000011240a160f05150a161005140600000000000000002403f70205940311250c030e030a161006140a16100514110d0a110a161002140a161003140a161004140a161005140b161006141201380a0596030b16010d250c1e05c8020b1e010b1a010b1101020500000102211a11260c0211132a021007140c0311270c010b01030c05110b030b02230c000513090c000b0003160518110c05191104020601000102010f0b00110111021112070c210309070827070911281105070a1128020701000102010511132b02100714020801000102222511132a020f000c090b090c080a000c070b000c060b080b070600000000000000000600000000000000000600000000000000000600000000000000000600000000000000000b06120338000c0a0a0a1002140a0a1003140a0a100a140a0a1004140b0a100514020901000102232611132a020f000c03400500000000000000000c060a032e38060c000d000c040a042e3807031105200a030b04380838090c05010c020d060b0210061444050d050c04050c0b04010b03010b06020a01000024220b00110111250c0a0e0a0c02380b0c0111250c040e04380c0c0511250c060e06380d0c0711250c080e08380e0c090b020b010600000000000000000b070b090b0512022d02070b1128020b010000010b0b0011011112070d210308070827070c1128020c00000102274a11132a020c0a0a0a0f000c070b0a0f090c050a072e38060c000d000c0811250c010e010c0b0a082e3807031805410a070b0838080c030c020b022e0b03380f0c09010c060a0b0a061006140a06100314110d0a050a061002140a061003140a061004140a061005140b061006141201380a0d090c0805130b0b010b08010b07010b0501020d00000001050b000b010b023803020e010000010b0b0011011112070a210308070627070b1128020f010000010b0b0011011112070b210308070727070d1128021001000020090b00110111250c030e030b010b023803021101000020090b00110111250c030e030b010b02381002020002040300030103030304030502010202020303020000"; - let res = run_binary_test("sample_aptosd_swap", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_router() { - let code = "a11ceb0b050000000a01000e020e0603148f0104a3012805cb018f0207da03f90208d30640069307a00110b3082b0cde08db110000010101020003000400050006010a0401000100070000000008010202000000090102020000020b050600050c060700050d050200030e0207020000040f02070200000410020a0200000111020c01000612000d000113020f01000414020a0200000115100f0100031611120200000117130201000418020d0200000519140d00041a1500020000041b020a020000041c020d020000051d140d00031e111202000006080609070907080808090b0b0e0c080d0b0e080f0b100812080f0e0809090e130914091209160802040403060c030300030404043501010101010103030404040404040404040b000109000b000109000b000109000b000109000b000109000b000109000b000109010b000109010b0001090103030303030303030404030301010404010404040503030303030301060c0105010102090009010209010900010301090001020104010901010b0001090002060c0308060c070b00010900070b00010901030301030302030302050b000109000305040106060c010303030432010103030404040404040404040b000109000b000109000b000109000b000109000b000109000b000109000b000109010b000109010b000109010303030303030404030301010404010404040505030303030303030306726f7574657204636f696e067369676e657203616d6d0b636c6f625f6d61726b657403666565047574696c0873696d706c69667918737761705f636f696e5f666f725f65786163745f636f696e18737761705f65786163745f636f696e5f666f725f636f696e04436f696e0a616464726573735f6f660a6665655f65786973747316696e697469616c697a655f6665655f64656661756c740b706f6f6c5f6578697374730d6d61726b65745f657869737473086c6f745f73697a6508646563696d616c7303657870047a65726f0c6e5f6269645f6c6576656c730877697468647261771d636f696e5f737761705f636f696e5f666f725f65786163745f636f696e076465706f7369740b626573745f6269645f61750c73756274726163745f66656516706c6163655f6f726465725f666f725f726f757465720c6e5f61736b5f6c6576656c730b626573745f61736b5f6175076164645f6665651d636f696e5f737761705f65786163745f636f696e5f666f725f636f696e49661cd59c0b89440313af587bc99c3d38614d9a52479eb3f7c7c766d580c30b00000000000000000000000000000000000000000000000000000000000000010308c9000000000000000308ca000000000000000308c800000000000000030804000000000000000308070000000000000003086500000000000000030866000000000000000308030000000000000003080200000000000000030864000000000000000308ffffffffffffffff0308050000000000000003080100000000000000030868000000000000000308670000000000000003080600000000000000126170746f733a3a6d657461646174615f7630170104000000000000000c45544553545f4641494c4544000001000003190a000c030a010c040a01320000000000000000000000000000000022030905120b000a01190c020b010c000b020c0105040b030a001a0b040b001a020101040004ec030a0011030c310a311104200308050a0a0011053800030d0510080c03051238010c030b030c2d38020c2a38030c290a2d0b291f031d05fb0138040c27320a000000000000000000000000000000380535110a0c0e0600000000000000000c320600000000000000000c3438060c1a0a340a0223032f05340a320a01230c060536090c060b06033905eb01380706000000000000000021033e055f0a000a010a321738080c140b000d140d1a0a010a32170a020a34170906000000000000000006000000000000000038090c210c1d0a310b14380a0b320b21160c320b340b1d160c3405eb01380b0c130a310a130811110c120a020a3417350a0e180a121a340c090b090a272303750596010a000a010a321738080c180b000d180d1a0a010a32170a020a34170906000000000000000006000000000000000038090c230c1f0a310b18380a0b320b23160c320b340b1f160c3405eb010a0e0a1211000c250c2b0a000a010a321738080c190a000d190d1a0a010a32170a020a3417080b2b340b253438090c240c200a310b19380a0b320b24160c320b340b20160c340a340a022303c20105c7010a320a01230c0805c901090c080b0803cc0105ea010a020a3417350a0e180b121a340c0a0a000907060b13340b0a3200000000000000000000000000000000380c0c2e0c0d0b320b0d34160c320b340b2e34160c34052a0b320b012503f1010707270b340b022103f7010708270b310b1a380d05eb030a2d0a2a1f03800205b203380e0c28320a000000000000000000000000000000380f35110a0c0f0600000000000000000c330600000000000000000c3538060c1b0a350a02230392020597020a330a01230c04059902090c040b04039c0205a20338100600000000000000002103a10205a402080c0505aa020a020a35170a28230c050b0503ad0205ce020a000a010a331738080c150b000d150d1b0a010a33170a020a35170906000000000000000006000000000000000038090c220c1e0a310b15380a0b330b22160c330b350b1e160c3505a20338110c110a310a110811150c100b100a0f11000c260c2c0a000a010a331738080c160a000d160d1b0a010a33170a020a3517080b2c340b263438090c360c370a310b16380a0b330b36160c330b350b37160c350a350a02230381030586030a330a01230c07058803090c070b07038b0305a1030a000807060b11340a020a3517320000000000000000000000000000000038120c2f0c0b0b330b2f34160c330b350b0b34160c35058d020b330b012503a8030707270b350b022103ae030708270b310b1b380d05eb030b2d03b50305cd030a000a0138080c1738060c1c0b000d170d1c0b010b0209060000000000000000060000000000000000380901010a310b17380a0b310b1c380d05eb030b2a03d00305e7030b00080705070a0a02320000000000000000000000000000000038120c300c0c0b0c340b022103e0030707270b30340b012503eb030708270b0001070c27020201040016d1030a0011030c2b0a2b1104200308050a0a0011053800030d0510080c03051238010c030b030c2738020c2438030c230a270a231f031d05b70138040c21320a000000000000000000000000000000380535110a0c0a0600000000000000000c2d0600000000000000000c2f38060c160a2d0a0123032f05a70138070600000000000000002103340537080c04053d0a010a2d170a21230c040b040340055f0a000a010a2d1738080c100b000d100d160a010a2d170600000000000000000906000000000000000006000000000000000038130c1c0c190a2b0b10380a0b2d0b1c160c2d0b2f0b19160c2f05a701380b0c0f0a2b0a0f0811110c0e0b0e0a0a11000c1f0c250a000a010a2d1738080c140a000d140d160a010a2d17060000000000000000080b25340b1f3438130c310c330a2b0b14380a0b2d0b31160c2d0b2f0b33160c2f0a2d0a012303900105a6010a000907060b0f340a010a2d173200000000000000000000000000000000380c0c280c080b2d0b0834160c2d0b2f0b2834160c2f052a0b2d0b012103ad010707270b2f0b022603b3010708270b2b0b16380d05d0030a270b241f03bc01059403380e0c22320a000000000000000000000000000000380f35110a0c0b0600000000000000000c2e0600000000000000000c3038060c180a2e0a012303ce0105840338100600000000000000002103d30105f2010a000a010a2e1738080c150b000d150d180a010a2e170600000000000000000906000000000000000006000000000000000038130c1d0c1a0a2b0b15380a0b2e0b1d160c2e0b300b1a160c3005840338110c0d0a2b0a0d0811150c0c0a010a2e17350a0b180a0c1a340c050b050a222303880205a7020a000a010a2e1738080c110b000d110d180a010a2e170600000000000000000906000000000000000006000000000000000038130c1e0c1b0a2b0b11380a0b2e0b1e160c2e0b300b1b160c300584030a0b0a0c11000c200c260a000a010a2e1738080c120a000d120d180a010a2e17060000000000000000080b26340b203438130c320c340a2b0b12380a0b2e0b32160c2e0b300b34160c300a2e0a012303d1020583030a010a2e17350a0b180b0c1a340c060a000807060b0d340a06320000000000000000000000000000000038120c2a0c070a07340b062503ee020b0001061111000000000000270a2a340a010a2e172503f9020b0001062222000000000000270b2e0b2a34160c2e0b300b0734160c3005c9010b2e0b0121038a030707270b300b02260390030708270b2b0b18380d05d0030b2703970305b2030a000a0138080c1338060c170a000d130d170b010b0209060000000000000000060000000000000000381301010b0011030c2c0a2c0b17380d0b2c0b13380a05d0030b2303b50305cc030b000907050600000000000000000a013200000000000000000000000000000000380c0c290c090b09340b012103c5030707270b29340b022603d0030708270b0001070c270200"; - let res = run_binary_test("sample_router", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_pool() { - let code = "a11ceb0b050000000c010020022079039901c90204e2036605c804fd0507c50aa80c08ed166006cd17c101108e19bb020ac91bd4020c9d1eb3250dd043340000010101020103010401050106010701080109010a020b000c000d000e000f0010070000110600001206000013060000140600001508000000040000160800001708000018060000190600001a0600001b0600071d07000b2707000b3a0700053c040106010344040100010245080009470402030100010b480400084e0402030100010b6c07000e7807000e7b07000e7e07000e80010700001c000100001e020100001f030100002002010000210201000022020100002300010000240201000025040100002605010000280607000029080100002a090100002b0a0100002c0b01000658050d0008590f07020300045a101000085b1213020300045c101000035d15160100035e17010100035f1810010005601a0101060b611e1c000b621f06000b632010000b64212200096524010203000a66250101000867122a0203000a682c2d01000a672e2f01000967312a0203000b69320100096a3301020300036b340101000d6d393a000c6d393a0009593b070203000b6e1c3c000b6f3d10000b703d10000b713d0d0003723e16010007733f1d000d74393a000c74393a000975013302030408654401020300087501460203040176054701060b7706100008790f490203000e7a4b4a000e7c4c4d000f7d4e0101040e7f3951000e81010e5300100e1011121114141514161417191c231d1c17261728120e1e111f2b202b12302123232324141735173617371f1c201c27232c1417403023103031303111310e17453211320e32303345333733263319333533363328334035303511384d384a3851350e385303060c04030006060c040a050a080d0a080d0a0306060c04020303030b060c020a050a080d0a080d0a03030303030201060c01080e010103060c05080d02060c0407060c03030303030302060c05040b11010812070805070806070808010502050a0402060b150209000901090001030204080602070b15020900090109000107090101081202060c03010b1101090002070b110109000b1101090001060b1101090001080202070b100109000900070708050303070806070808080f0a080f01080f01080d0405080d080d030305080d080d0205080f03060c080f0301081402080f081403070b1302090009010900090102070a09000a0900010803030708050708060708080108041a06040604030b1101081205080d070a0405020307080503030303030307080803080f0a080f0b1302080f0814040302070a04010901010402060a090006090002010302070a0900030109000208000a0402070b130209000901090002060c0814010b13020900090102050b1101090001080c01080b0108092c03030303030816050405080d040302030a080f0303030503080d03070b1001080a020b11010812030307080503030303030303070806070b150204080603060816030503080f0a080f0503030303030602030303030302060b13020900090109000108160106081602070b1101090003010a0201080a2b03030303030816050405080d040302030a080f0303030503080d03070b1001080a020b110108120307080503030303030303070806070b150204080603060816030503080f0a080f08070805030303070806070808080f0a080f0d01050b1101081206080d060507080507040303070808080f0a080f0b1302080f081403070b15020900090109000901010801010b150209000901010b10010900080800030403060a040608060708080a0817010609010108170d05030305080d020303020a080f0304030405080d0a040a081701081802060c09000206080607080817030303020303030303020303030303020303030303030301081902060a0407080801081a04506f6f6c076163636f756e740a6170746f735f636f696e04636f696e056572726f72056576656e74067369676e657206737472696e67057461626c65117461626c655f776974685f6c656e67746806766563746f7205746f6b656e104578706f6e656e7469616c43757276650b4c696e6561724375727665054d6f64656c04566965770c436f6c6c656374696f6e49640f437265617465506f6f6c4576656e74144465706f736974436f696e506f6f6c4576656e74154465706f736974546f6b656e506f6f6c4576656e740d45646974506f6f6c4576656e740a4576656e7453746f726508506f6f6c4775696405506f6f6c730f52656d6f7665506f6f6c4576656e7409537761704576656e74155769746864726177436f696e506f6f6c4576656e74165769746864726177546f6b656e506f6f6c4576656e740b4465706f736974436f696e06537472696e670c4465706f736974546f6b656e0845646974506f6f6c0a52656d6f7665506f6f6c1753776170436f696e466f7253706563696669634e4654730f537761704e465473466f72436f696e0c5769746864726177436f696e0d5769746864726177546f6b656e066372656174650b696e69745f6d6f64756c650b546f6b656e4461746149640a69735f6572633131353515766965775f636f6c6c656374696f6e5f706f6f6c7309766965775f706f6f6c0f766965775f70726963655f63616c630f766965775f757365725f706f6f6c7312636f6c6c656374696f6e5f63726561746f720f636f6c6c656374696f6e5f6e616d650763726561746f7207706f6f6c5f69640a73706f745f70726963650563757276650564656c7461116c705f6665655f6d756c7469706c69657204747970650673656e646572136465706f7369745f636f696e5f616d6f756e7405636f696e7309746f6b656e5f69647307546f6b656e49640b6372656174655f706f6f6c0b4576656e7448616e646c650b72656d6f76655f706f6f6c126465706f7369745f746f6b656e5f706f6f6c116465706f7369745f636f696e5f706f6f6c1377697468647261775f746f6b656e5f706f6f6c1277697468647261775f636f696e5f706f6f6c09656469745f706f6f6c047377617004436f696e094170746f73436f696e06746f6b656e730f5461626c65576974684c656e67746805546f6b656e0c7472616e73616374696f6e7317746f74616c5f7472616e73616374696f6e5f76616c75650f6c705f6665655f6561726e696e677305636f756e7405706f6f6c73055461626c650a757365725f706f6f6c7310636f6c6c656374696f6e5f706f6f6c730b636f696e5f616d6f756e740c70726f746f636f6c5f6665650e70726f746f636f6c5f70617965650b726f79616c74795f6665650d726f79616c74795f7061796565066c705f6665651477697468647261775f636f696e5f616d6f756e740a616464726573735f6f6608636f6e7461696e7310696e76616c69645f617267756d656e740a626f72726f775f6d7574117065726d697373696f6e5f64656e696564087769746864726177056d657267650576616c75650a656d69745f6576656e74136372656174655f746f6b656e5f69645f726177146372656174655f746f6b656e5f646174615f69640a62616c616e63655f6f660e77697468647261775f746f6b656e0361646406617070656e640672656d6f766508696e6465785f6f660d6465706f7369745f746f6b656e0d64657374726f795f656d707479076465706f73697407526f79616c74790a676574427579496e666f0b6765745f726f79616c7479176765745f726f79616c74795f64656e6f6d696e61746f72156765745f726f79616c74795f6e756d657261746f72116765745f726f79616c74795f7061796565076578747261637404757466380b67657453656c6c496e666f036e6577106e65775f6576656e745f68616e646c65156765745f746f6b656e646174615f6d6178696d756d08506f6f6c5669657706626f72726f7709706f6f6c5f7669657713436f6c6c656374696f6e506f6f6c735669657715636f6c6c656374696f6e5f706f6f6c735f7669657704766965770d507269636543616c63566965770f70726963655f63616c635f766965770d55736572506f6f6c73566965770f757365725f706f6f6c735f76696577ecb44cec6276ddc5860a13b976262c786e06af8f7306923cabec17808dabf0940000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000303086600000000000000030869000000000000000308680000000000000003086e000000000000000308670000000000000003086a0000000000000003086b0000000000000003086c000000000000000308650000000000000003086d000000000000000520ecb44cec6276ddc5860a13b976262c786e06af8f7306923cabec17808dabf09403086400000000000000052000000000000000000000000000000000000000000000000000000000000000000a0204036275790a02050473656c6c126170746f733a3a6d657461646174615f7630a6020a6500000000000000074552525f4e554d006600000000000000134552525f444946465f434f4c4c454354494f4e006700000000000000124552525f4e4f545f45584953545f504f4f4c006800000000000000154552525f494e56414c49445f504f4f4c5f54595045006900000000000000154552525f494e56414c49445f504f4f4c5f41524753006a000000000000001c4552525f4e4f545f4558495453545f544f4b454e5f494e5f504f4f4c006b00000000000000124552525f4e4f545f484156455f544f4b454e006c00000000000000144552525f4e4f545f504f4f4c5f43524541544f52006d00000000000000184552525f504f4f4c5f4e4f545f454e4f5547485f434f494e006e00000000000000114552525f49535f313135355f544f4b454e000002022d052e080d0102092f0530042d052e080d3103320233033403350202020a360530042d052e080d37033803310332023303340303020a360530042d052e080d390a080f3803310332023303340304020730042d052e080d31033202330334030502083b0b100108013d0b100108093e0b100108033f0b10010802400b1001080c410b1001080b420b10010804430b1001080a06020e2f053103380b11010812460b1302080f08142d052e080d3202330334033502390a080f49034a044b030702014c040802034d0b15020408064f0b1502050a04500b150208000a0409020c2f0530042d052e080d3103320233033403350249034a044b030a0210360530042d052e080d310332023303390a080f510352035305540355055603380335080d0b020a360530042d052e080d5703380331033202330334030c020a360530042d052e080d390a080f380331033202330334030001000205080c61070a2a080c06070a2a050c040a0610000a00110f3800040d05160b00010b06010b040107041111270a0610010a013801041c05250b00010b06010b040107041111270b060f010a0138020c050a051002140a00110f210432053b0b00010b05010b040107071113270a000a0238030c030a050f030b0338040b040f040b00110f0b010a051005140a051006140b020a05100338050a051007140a051008140a051009140b05100a1412023806020101000205081bd801070a2a080c0a070a2a050c060a0a10000a00110f3800040d05160b00010b0a010b060107041111270a0a10010a013801041c05250b00010b0a010b060107041111270b0a0f010a0138020c09401c00000000000000000c0c0a091002140a00110f210434053d0b00010b09010b060107071113270e02410d0c080600000000000000000c070a070a082304b60105470e020a07420d0a09100521044f05580b00010b09010b060107001111270e030a07421d0a09100621046005690b00010b09010b060107001111270e020a07420d140e030a07421d140e040a07421d140e050a0742101411180c0b0e020a07420d140e030a07421d140e040a07421d141119110a20048c010595010b00010b09010b060107031111270d0c0a0b441c0a00110f0a0b111a0601000000000000002604a00105a9010b00010b09010b060107061111270a090f0b0a0b0a000b0b060100000000000000111b38070b07060100000000000000160c0705420a090f0c0a0c38080b060f0d0b00110f0b010a091005140a091006140b0c0a09100338050a091007140a091008140a091009140b09100a1412033809020201000205082759070a2a080c08070a2a050c060a0810000a00110f3800040d05160b00010b08010b060107041111270a0810010a013801041c05250b00010b08010b060107041111270b080f010a0138020c070a071002140b00110f21043205390b07010b060107071113270a020a070f08150a040a070f09150a030a070f07150a050a070f0a150b060f0e0b010a071005140b071006140b030b020b040b051204380a0203010002050829dc01070a2a080c17070a2a050c100a1710000a00110f3800040d05160b00010b17010b100107041111270a170f000a00110f380b0c1f0a1710010a0138010422052d0b1f010b00010b17010b100107041111270a170f010a01380c13060c150c1c0c1d010c1e0c160c0f0c0e0c0b0c0a0c1b0c090c180c0d0a0d0a00110f21044605510b1f010b00010b17010b100107071113270a1f0e010c062e0b06380d0c12010b1f0b12380e010b170f0f0a0a0a0b1200380f0c0c0a0c0e010c072e0b07380d0c13046d05760b00010b10010b0c0107051111270b0c0b13380e010600000000000000000c110e02410d0c14401c00000000000000000c1a0a110a142304a5010586010e020a11420d140e030a11421d140e040a11421d140e050a1142101411180c190d1a0a19441c0a000d1b0b19381011220b11060100000000000000160c110581010b1b38110e0938050c080a00110f0b0938120a100f100a0d0a010a0a0a0b0b1a0600000000000000000600000000000000003100060000000000000000060000000000000000120c38130a100f110b0d0a010a0a0a0b0b080600000000000000000600000000000000003100060000000000000000060000000000000000120b38140b100f120b00110f0b010b0a0b0b0b180b0e0b0f0b160b1e0b1d0b1c0b15120938150204010002050838d502070a2a080f010c2a070a2a050c210a2a0a010c102e0b103801040f05180b00010b2a010b210107041111270e02410d0c270b2a0a0138020c290a2910081431012104380a291007140a291009140a270a29100a14070b11250c0a0c090c080c070c060c1d055a0a29100814310221043f05480b00010b29010b210107021111270a291007140a291009140a270a29100a14070b11260c0a0c090c080c070c060c1d0b1d0b060b070b080b090b0a0c240c2b0c1f0c250c26310021046905720b00010b29010b21010701111127070c0600000000000000000600000000000000000c2f0c2d0c2e0b260a290f07150b250a290f0915401c00000000000000000c310a000a1f38030c1e0600000000000000000c220a220a272304f401058d010e020a22420d140e030a22421d140e040a22421d140e050a2242101411180c300a29100c0e3038160c2304a60105af010b00010b29010b210107051111270a290f0c0b233817010d310a30441c0a29100b0a30381804bd0105c6010b00010b29010b210107051111270a000a290f0b0a30381011220b3011280c0b0e0b0c2c0a2c11290c200a2c112a0c280b2c112b0c2e0a200600000000000000002204ef010a1f0a271a0b28180b201a0c2d0b2f0a2d160c2f0a2e0d1e0a2d381938120b22060100000000000000160c220588010a291013140a1f35160a290f13150a29101414060100000000000000160a290f1415070a0d1e0a2b381938120a290f030b1e38040b210f150c1c0b00110f0c0c0b010c0d0a291005140c0e0a291006140c0f0a291007140c110a291008140c120a291009140c130b310c140b1f0a2b160b2d160c150b2b0c160b2f0c170b2e0c180b240c19070d112d0c1a0b29100338050c1b0b1c0b0c0b0d0b0e0b0f0b110b120b130b140b150b16070a0b170b180b190b1b0b1a120a381a02050100020508418c03070a2a080f010c29070a2a050c200a290a010c102e0b103801040f05180b00010b29010b200107041111270e02410d0c250b290a0138020c280a2810081431012104380a281007140a281009140a250a28100a14070b112e0c0a0c090c080c070c060c1d055a0a28100814310221043f05480b00010b28010b200107021111270a281007140a281009140a250a28100a14070b112f0c0a0c090c080c070c060c1d0b1d0b060b070b080b090b0a0c220c2a0c270c230c24310021046905720b00010b28010b20010701111127070c0600000000000000000600000000000000000c2e0c2c0c2d401c00000000000000000c300a280f030a270a2a1738190c1e0600000000000000000c210a210a2523049a020588010e020a21420d0a281005210490010599010b00010b28010b200107001111270e030a21421d0a2810062104a10105aa010b00010b28010b200107001111270e020a21420d140e030a21421d140e040a21421d140e050a2142101411180c2f0e020a21420d140e030a21421d140e040a21421d141119110a2004cd0105d6010b00010b28010b200107031111270d300a2f441c0a00110f0a2f111a0601000000000000002604e10105ea010b00010b28010b200107061111270a280f0b0a2f0a000a2f060100000000000000111b38070b2f11280c0b0e0b0c2b0a2b11290c1f0a2b112a0c260b2b112b0c2d0a1f060000000000000000220495020a270a251a0b26180b1f1a0c2c0b2e0a2c160c2e0a2d0d1e0a2c381938120b21060100000000000000160c210583010a280f0c0a303808070a0a280f030a2a381938120b240a280f07150b230a280f09150a00110f0b1e38120a281013140a270a2a170b2e1735160a280f13150a28101414060100000000000000160a280f14150b200f150c1c0b00110f0c0c0b010c0d0a281005140c0e0a281006140c0f0a281007140c110a281008140c120a281009140c130b300c140b270a2a170a2c170c150b2a0c160b2c0c170b2d0c180b220c19070e112d0c1a0b28100338050c1b0b1c0b0c0b0d0b0e0b0f0b110b120b130b140b150b16070a0b170b180b190b1b0b1a120a381a020601000205082770070a2a080c05070a2a050c030a0510000a00110f3800040d05160b00010b05010b030107041111270a0510010a013801041c05250b00010b05010b030107041111270b050f010a0138020c040a041002140a00110f210432053b0b00010b04010b030107071113270a04100338050a02260442054b0b00010b04010b030107091111270a00110f0a040f030a02381938120b030f110b00110f0b010a041005140a041006140b020a04100338050a041007140a041008140a041009140b04100a14120b38140207010002050842a901070a2a080c0b070a2a050c060a0b10000a00110f3800040d05160b00010b0b010b060107041111270a0b10010a013801041c05250b00010b0b010b060107041111270b0b0f010a0138020c0a401c00000000000000000c0d0a0a1002140a00110f210434053d0b00010b0a010b060107071113270e02410d0c090600000000000000000c070a070a0923048b0105470e020a07420d140e030a07421d140e040a07421d140e050a0742101411180c0c0a0a100c0e0c38160c08046005690b00010b0a010b060107051111270a0a0f0c0b083817010d0d0a0c441c0a0a100b0a0c381804770580010b00010b0a010b060107051111270a000a0a0f0b0b0c381011220b07060100000000000000160c0705420b060f100b00110f0b010a0a1005140a0a1006140b0d0a0a100338050a0a1007140a0a1008140a0a1009140b0a100a14120c38130208010003050708439f02070a2a080c14070a2a070f160c11070a2a050c10381b0c170e02410d0c130a13060000000000000000240414051f0b00010b14010b11010b100107081111270e02060000000000000000420d0c0f0e03060000000000000000421d0c0e0a14100f0a0f140a0e141200381c20043a0a140f0f0a0f140a0e141200402b0000000000000000381d0a140f0f0a0f140a0e141200380f0a1114442b401c00000000000000000c160600000000000000000c120a120a132304b001054e0a0f0e020a12420d21045b0a0e0e030a12421d210c0b055d090c0b0b0b0460056f0b00010b14010b11010b10010b0f010b0e0107001111270e020a12420d140e030a12421d140e040a12421d141119110a20048001058f010b00010b14010b11010b10010b0f010b0e0107031111270e020a12420d140e030a12421d140e040a12421d140e050a1242101411180c150d160a15441c0d170a150a000b15060100000000000000111b38070b12060100000000000000160c1205490a100f0d0a00110f0a11140a0f140a0e140a160a060a070a010a080a09120338090a000a0638030c0d0a100f040a00110f0a11140a0f140a0e140a060b060a070a010a080a09120238060a140f010a11140a00110f0a070b0d0b170a0f140a0e140a010a080a090a0a0b1606000000000000000032000000000000000000000000000000000600000000000000001206381e0a140f000a00110f0c0c2e0b0c38002004ff010a140f000a00110f402b0000000000000000381f0b140f000a00110f380b0a1114442b0b100f170b00110f0a11140b0f140b0e140b070b010b080b090b0a120138200a11143201000000000000000000000000000000160b11150209000000011e0a0038213822382312082d080a00320000000000000000000000000000000012072d070a000a0038240a0038250a0038260a0038270a0038280a0038290a00382a0b00382b12052d05020a00000001060601000000000000000b0011342120020b010401084876070a2a080c090a010a0212000c030a09100f0a03381c040d05130b00010b0901063f05000000000000270a09100f0b03382c0c07404a00000000000000000c0a0a07412b0c060600000000000000000c040a040a0623046b05240a070a04422b140c050a0910010a053801042f05370b00010b09010b0701063f05000000000000270a0910010b05382d0c080d0a0a081002140a081007140a08100338050a081005140a081006140a081008140a081009140a08100a140a081018140a08100c140a081014140a081013140b081019141136444a0b04060100000000000000160c04051f0b09010b000b010b020b07140b0a1137382e020c010401084f40070a2a080c030a0310010a013801040905100b00010b030107041111270b0310010b01382d0c020b000a021002140a021007140a02100338050a021005140a021006140a021008140a021009140a02100a140a021018140a02100c140a021014140a021013140b021019141136382f020d0104005087010b010600000000000000002104380a020601000000000000002104150b030b040b050b06070b11250c090c080c070c180c170c16052b0b0206020000000000000021041a051f0b000107021111270b030b040b050b06070b11260c090c080c070c180c170c160b160b170b180b070b080b090c150c140c130c120c110c10056b0a020601000000000000002104490b030b040b050b06070b112e0c0f0c0e0c0d0c0c0c0b0c0a055f0b0206020000000000000021044e05530b000107021111270b030b040b050b06070b112f0c0f0c0e0c0d0c0c0c0b0c0a0b0a0b0b0b0c0b0d0b0e0b0f0c150c140c130c120c110c100b100b110b120b130b140b150c1a0c1d0c190c1b0c1c310021047a057e0b0001064705000000000000270b000b1c0b1b0b190b1d0b1a11393830020e01040108521b070a2a080c030a0310000a0138000409050f0b00010b0301063f05000000000000270b0310000a0138310c020b000b010b0214113a383202080108000600060205030604060506010606060706080603060a050205060802050405050501060c060b0507070005000609060d00"; - let res = run_binary_test("sample_pool", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_farming() { - let code = "a11ceb0b050000000e01001e021e2603447c04c00158059802c80107e003bb04089b08a00106bb09e101109c0b780a940c3e0bd20c040cd60cd10d0da71a080eaf1a060000010101020103010401050106000700080209020a0309030a0409040a000b0800000c0e0200010001000d0c0200010001011106000513070002230401000107240000000e000100000f0201020000032104050001220607000525090a000e26010c0200000c26010c0200000a26010c0200000427001200022812140100022915160100022a17010100082b150101000d2c1a010200000b2c1a01020000092c1a010200000d2d1b010200000b2d1b01020000092d1b01020000062e011400050b050d050e050f05100511060b060d060e060f06100611070b070d070e070f0710071109130a130b1309180a180b1809190a190b190c130c180d0b0e0b0f0b0d0d0e0d0f0d0d0e0d100e0e0e100f0e0f10100b110b120b01060c0009060c0303030303030303020c080302060c0501080301060803010c2001010101010101010101030303030308040b050108060b050109000b050109010708000b0102090009010b0102090009010b020209000901070b020209000901050c0a0b0102090009010503030303010a0201080402090009010101020901090002080609000209000806020806090102090108060105010900010302060c03010b0501090002050b0501090001090101080603060c030305060c03030303010b010209000901076661726d696e67076163636f756e7404636f696e107265736f757263655f6163636f756e74067369676e657206737472696e670974696d657374616d700d6661756365745f746f6b656e73076c656e64696e6706726f7574657204737761700a4d6f64756c65446174610c506f736974696f6e496e666f0f506f736974696f6e496e666f4465780b696e69745f6d6f64756c65166c657665726167655f7969656c645f6661726d696e670a7369676e65725f636170105369676e65724361706162696c697479086465785f6e616d6506537472696e670b7369676e65725f616464720a637265617465645f617406737461747573086c657665726167650e737570706c79416d6f756e745f780e737570706c79416d6f756e745f790e737570706c79416d6f756e745f7a0e626f72726f77416d6f756e745f780e626f72726f77416d6f756e745f790e626f72726f77416d6f756e745f7a10706f736974696f6e496e666f5f70616e10706f736974696f6e496e666f5f6c697110706f736974696f6e496e666f5f6175781d72657472696576655f7265736f757263655f6163636f756e745f6361701d6372656174655f7369676e65725f776974685f6361706162696c69747904436f696e03455a4d04757466380f69735f706169725f637265617465640a616464726573735f6f660762616c616e6365087769746864726177076465706f73697406626f72726f7710737761705f65786163745f696e7075740d6164645f6c69717569646974790b6e6f775f7365636f6e64734d63574ba8d90a5926e2866d8291e57683108a47b96d884232a871fe4feddf4100000000000000000000000000000000000000000000000000000000000000015e40a5bf7ab741784d3a99d96d8e2ddc6706ee06e5f509a3314a74c9e53746f5b30dd6a14dd7626ac8a37bc964914f07e3dc38d629637f1087f9f7186f1e0909c4911c40cf758ec21c0ebf0e547933ef6bb0f53ad581c08d2ecc7ad11364be1b030804000000000000000520f67b2452fd82768002ec6c28568713b9359a596585dc1a4bf85fce6b0e3754020308020000000000000003080300000000000000030801000000000000000308ffffffffffffffff0410e80300000000000000000000000000000308000000000000000005204d63574ba8d90a5926e2866d8291e57683108a47b96d884232a871fe4feddf41052000000000000000000000000000000000000000000000000000000000000000000a020c0b50616e63616b65537761700a020b0a4c6971756964537761700a020d0c4155582045786368616e6765126170746f733a3a6d657461646174615f763064030100000000000000164552524f525f4e4f545f435245415445445f50414952000200000000000000184552524f525f494e53554646494349454e545f4153534554000300000000000000174552524f525f494e56414c49445f504152414d5f4445580000020110080301020b1208041405150316011703180319031a031b031c031d030202031e0a0b0102090009011f0a0b010209000901200a0b010209000901020b010b00000000030c0b00070111020c020e0211030c010e010b0212002d000201010402000208ee04070a1104010a010600000000000000002103080536070a11040c183800030e0511080c09051338010c090b0903190b00010704273802031c051f080c0b052138030c0b0b0b03270b00010704273804032a052d080c0c052f38050c0c0b0c03350b0001070427059f010a0106010000000000000021033b0569070b11040c18380603410544080c0d054638070c0d0b0d034c0b00010704273808034f0552080c0e055438090c0e0b0e035a0b0001070427380a035d0560080c0f0562380b0c0f0b0f03680b0001070427059f010a0106020000000000000021036e059b01070c11040c18380c03740577080c100579380d0c100b10037f0b0001070427380e038201058501080c11058701380f0c110b11038d010b00010704273810039001059301080c1205950138110c120b12039f010b00010704270b000107032707082a000c1c0b1c100011030c220e2211080c210a030600000000000000002403ae0105c1010a00110838120c160b160a032403ba010b00010702270a000a0338130c1a07080b1a38140a040600000000000000002403c60105d9010a00110838150c170b170a042403d2010b00010702270a000a0438160c1b07080b1b38170a050600000000000000002403de0105f1010a00110838180c150b150a052403ea010b00010702270a000a0538190c1907080b19381a0a060600000000000000002403f60105f9010e220a06381b0a070600000000000000002403fe010581020e220a07381c0a0806000000000000000024038502070838120c26070838150c280a030a06160602000000000000001a0600000000000000002403940205bd020a010600000000000000002103990205a2020e220a030a06160602000000000000001a060000000000000000381d05bd020a010601000000000000002103a70205b0020e220a030a06160602000000000000001a060000000000000000381e05bd020a010602000000000000002103b50205bd020e220a030a06160602000000000000001a060000000000000000381f0a040a07160602000000000000001a0600000000000000002403c60205ef020a010600000000000000002103cb0205d4020e220a040a07160602000000000000001a060000000000000000382005ef020a010601000000000000002103d90205e2020e220a040a07160602000000000000001a060000000000000000382105ef020a010602000000000000002103e70205ef020e220a040a07160602000000000000001a06000000000000000038220a050a08160602000000000000001a0600000000000000002403f80205b9030a010600000000000000002103fd02058e030e220a050a08160602000000000000001a06000000000000000038230e220a050a08160602000000000000001a060000000000000000382405b9030a010601000000000000002103930305a4030e220a050a08160602000000000000001a06000000000000000038250e220a050a08160602000000000000001a060000000000000000382605b9030a010602000000000000002103a90305b9030e220a050a08160602000000000000001a06000000000000000038270e220a050a08160602000000000000001a0600000000000000003828070838120c25070838150c270a030b25160b26170c130a040b27160b28170c140a130600000000000000002403d00305d5030a14060000000000000000240c0a05d703090c0a0b0a03da0305eb040a010600000000000000002103df0305e6030e220b130b14060000000000000000060000000000000000382905fd030a010601000000000000002103eb0305f2030e220b130b14060000000000000000060000000000000000382a05fd030a010602000000000000002103f70305fd030e220b130b14060000000000000000060000000000000000382b0b0011080c240a213b002003850405ba040b180b241113080b020b030b040b050b060b070b0839010c1d401c000000000000000001401c0000000000000000401c0000000000000000401c000000000000000039000c1f0a0106000000000000000021039e0405a3040d1f36000b1d441c05b6040a010601000000000000002103a80405ad040d1f36010b1d441c05b6040b010602000000000000002103b20405b6040d1f36020b1d441c0e220b1f3f0005ea040b213c000c200b180b241113080b020b030b040b050b060b070b0839010c1e0a010600000000000000002103cf0405d4040b2036000b1e441c05ea040a010601000000000000002103d90405de040b2036010b1e441c05ea040b010602000000000000002103e30405e8040b2036020b1e441c05ea040b200105ed040b0001020000020002010202010b020b030b00"; - let res = run_binary_test("sample_farming", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_whitelist() { - let code = "a11ceb0b050000000c01000602060c03123a044c0805545707ab01660891024006d102988b0210e98d02d7010ac08f02090cc98f02a2470debd6020200000101010200030800020a04020301000100040001000005020100000603040000070201000008000100010b0203000204070102030002060a04020300020c010c02030402080d0e020300060607060806090602060c050001060c01050101020507080002050203070b010209000901090009010305070b010205020708000106080002060b0102090009010900010b01020502010b01020900090102070b01020900090109000109010977686974656c697374067369676e6572057461626c650957686974656c69737403616464086164645f6d616e7908636f6e7461696e730a696e697469616c697a650672656d6f766507656e7472696573055461626c650a616464726573735f6f66036e65774419523fdfa01e96fc7c18b67d900be8c863459e25383a5c661b9523631811c1000000000000000000000000000000000000000000000000000000000000000103082003000000000000030822030000000000000308210300000000000005204419523fdfa01e96fc7c18b67d900be8c863459e25383a5c661b9523631811c1052000000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000105200000000000000000000000000000000000000000000000000000000000000002052000000000000000000000000000000000000000000000000000000000000000030520000000000000000000000000000000000000000000000000000000000000000405200000000000000000000000000000000000000000000000000000000000000005052000000000000000000000000000000000000000000000000000000000000000060520000000000000000000000000000000000000000000000000000000000000000705200000000000000000000000000000000000000000000000000000000000000008052000000000000000000000000000000000000000000000000000000000000000090520000000000000000000000000000000000000000000000000000000000000001005200000000000000000000000000000000000000000000000000000000000000011052000000000000000000000000000000000000000000000000000000000000000120520000000000000000000000000000000000000000000000000000000000000001305200000000000000000000000000000000000000000000000000000000000000014052000000000000000000000000000000000000000000000000000000000000000150520000000000000000000000000000000000000000000000000000000000000001605200000000000000000000000000000000000000000000000000000000000000017052000000000000000000000000000000000000000000000000000000000000000180520000000000000000000000000000000000000000000000000000000000000001905200000000000000000000000000000000000000000000000000000000000000020052000000000000000000000000000000000000000000000000000000000000000210520000000000000000000000000000000000000000000000000000000000000002205200000000000000000000000000000000000000000000000000000000000000023052000000000000000000000000000000000000000000000000000000000000000240520000000000000000000000000000000000000000000000000000000000000002505200000000000000000000000000000000000000000000000000000000000000026052000000000000000000000000000000000000000000000000000000000000000270520000000000000000000000000000000000000000000000000000000000000002805200000000000000000000000000000000000000000000000000000000000000029052000000000000000000000000000000000000000000000000000000000000000300520000000000000000000000000000000000000000000000000000000000000003105200000000000000000000000000000000000000000000000000000000000000032052000000000000000000000000000000000000000000000000000000000000000330520000000000000000000000000000000000000000000000000000000000000003405200000000000000000000000000000000000000000000000000000000000000035052000000000000000000000000000000000000000000000000000000000000000360520000000000000000000000000000000000000000000000000000000000000003705200000000000000000000000000000000000000000000000000000000000000038052000000000000000000000000000000000000000000000000000000000000000390520000000000000000000000000000000000000000000000000000000000000004005200000000000000000000000000000000000000000000000000000000000000041052000000000000000000000000000000000000000000000000000000000000000420520000000000000000000000000000000000000000000000000000000000000004305200000000000000000000000000000000000000000000000000000000000000044052000000000000000000000000000000000000000000000000000000000000000450520000000000000000000000000000000000000000000000000000000000000004605200000000000000000000000000000000000000000000000000000000000000047052000000000000000000000000000000000000000000000000000000000000000480520000000000000000000000000000000000000000000000000000000000000004905200000000000000000000000000000000000000000000000000000000000000050052000000000000000000000000000000000000000000000000000000000000000510520000000000000000000000000000000000000000000000000000000000000005205200000000000000000000000000000000000000000000000000000000000000053052000000000000000000000000000000000000000000000000000000000000000540520000000000000000000000000000000000000000000000000000000000000005505200000000000000000000000000000000000000000000000000000000000000056052000000000000000000000000000000000000000000000000000000000000000570520000000000000000000000000000000000000000000000000000000000000005805200000000000000000000000000000000000000000000000000000000000000059052000000000000000000000000000000000000000000000000000000000000000600520000000000000000000000000000000000000000000000000000000000000006105200000000000000000000000000000000000000000000000000000000000000062052000000000000000000000000000000000000000000000000000000000000000630520000000000000000000000000000000000000000000000000000000000000006405200000000000000000000000000000000000000000000000000000000000000065052000000000000000000000000000000000000000000000000000000000000000660520000000000000000000000000000000000000000000000000000000000000006705200000000000000000000000000000000000000000000000000000000000000068052000000000000000000000000000000000000000000000000000000000000000690520000000000000000000000000000000000000000000000000000000000000007005200000000000000000000000000000000000000000000000000000000000000071052000000000000000000000000000000000000000000000000000000000000000720520000000000000000000000000000000000000000000000000000000000000007305200000000000000000000000000000000000000000000000000000000000000074052000000000000000000000000000000000000000000000000000000000000000750520000000000000000000000000000000000000000000000000000000000000007605200000000000000000000000000000000000000000000000000000000000000077052000000000000000000000000000000000000000000000000000000000000000780520000000000000000000000000000000000000000000000000000000000000007905200000000000000000000000000000000000000000000000000000000000000080052000000000000000000000000000000000000000000000000000000000000000810520000000000000000000000000000000000000000000000000000000000000008205200000000000000000000000000000000000000000000000000000000000000083052000000000000000000000000000000000000000000000000000000000000000840520000000000000000000000000000000000000000000000000000000000000008505200000000000000000000000000000000000000000000000000000000000000086052000000000000000000000000000000000000000000000000000000000000000870520000000000000000000000000000000000000000000000000000000000000008805200000000000000000000000000000000000000000000000000000000000000089052000000000000000000000000000000000000000000000000000000000000000900520000000000000000000000000000000000000000000000000000000000000009105200000000000000000000000000000000000000000000000000000000000000092052000000000000000000000000000000000000000000000000000000000000000930520000000000000000000000000000000000000000000000000000000000000009405200000000000000000000000000000000000000000000000000000000000000095052000000000000000000000000000000000000000000000000000000000000000960520000000000000000000000000000000000000000000000000000000000000009705200000000000000000000000000000000000000000000000000000000000000098052000000000000000000000000000000000000000000000000000000000000000990520000000000000000000000000000000000000000000000000000000000000010005200000000000000000000000000000000000000000000000000000000000000101052000000000000000000000000000000000000000000000000000000000000001020520000000000000000000000000000000000000000000000000000000000000010305200000000000000000000000000000000000000000000000000000000000000104052000000000000000000000000000000000000000000000000000000000000001050520000000000000000000000000000000000000000000000000000000000000010605200000000000000000000000000000000000000000000000000000000000000107052000000000000000000000000000000000000000000000000000000000000001080520000000000000000000000000000000000000000000000000000000000000010905200000000000000000000000000000000000000000000000000000000000000110052000000000000000000000000000000000000000000000000000000000000001110520000000000000000000000000000000000000000000000000000000000000011205200000000000000000000000000000000000000000000000000000000000000113052000000000000000000000000000000000000000000000000000000000000001140520000000000000000000000000000000000000000000000000000000000000011505200000000000000000000000000000000000000000000000000000000000000116052000000000000000000000000000000000000000000000000000000000000001170520000000000000000000000000000000000000000000000000000000000000011805200000000000000000000000000000000000000000000000000000000000000119052000000000000000000000000000000000000000000000000000000000000001200520000000000000000000000000000000000000000000000000000000000000012105200000000000000000000000000000000000000000000000000000000000000122052000000000000000000000000000000000000000000000000000000000000001230520000000000000000000000000000000000000000000000000000000000000012405200000000000000000000000000000000000000000000000000000000000000125052000000000000000000000000000000000000000000000000000000000000001260520000000000000000000000000000000000000000000000000000000000000012705200000000000000000000000000000000000000000000000000000000000000128052000000000000000000000000000000000000000000000000000000000000001290520000000000000000000000000000000000000000000000000000000000000013005200000000000000000000000000000000000000000000000000000000000000131052000000000000000000000000000000000000000000000000000000000000001320520000000000000000000000000000000000000000000000000000000000000013305200000000000000000000000000000000000000000000000000000000000000134052000000000000000000000000000000000000000000000000000000000000001350520000000000000000000000000000000000000000000000000000000000000013605200000000000000000000000000000000000000000000000000000000000000137052000000000000000000000000000000000000000000000000000000000000001380520000000000000000000000000000000000000000000000000000000000000013905200000000000000000000000000000000000000000000000000000000000000140052000000000000000000000000000000000000000000000000000000000000001410520000000000000000000000000000000000000000000000000000000000000014205200000000000000000000000000000000000000000000000000000000000000143052000000000000000000000000000000000000000000000000000000000000001440520000000000000000000000000000000000000000000000000000000000000014505200000000000000000000000000000000000000000000000000000000000000146052000000000000000000000000000000000000000000000000000000000000001470520000000000000000000000000000000000000000000000000000000000000014805200000000000000000000000000000000000000000000000000000000000000149052000000000000000000000000000000000000000000000000000000000000001500520000000000000000000000000000000000000000000000000000000000000015105200000000000000000000000000000000000000000000000000000000000000152052000000000000000000000000000000000000000000000000000000000000001530520000000000000000000000000000000000000000000000000000000000000015405200000000000000000000000000000000000000000000000000000000000000155052000000000000000000000000000000000000000000000000000000000000001560520000000000000000000000000000000000000000000000000000000000000015705200000000000000000000000000000000000000000000000000000000000000158052000000000000000000000000000000000000000000000000000000000000001590520000000000000000000000000000000000000000000000000000000000000016005200000000000000000000000000000000000000000000000000000000000000161052000000000000000000000000000000000000000000000000000000000000001620520000000000000000000000000000000000000000000000000000000000000016305200000000000000000000000000000000000000000000000000000000000000164052000000000000000000000000000000000000000000000000000000000000001650520000000000000000000000000000000000000000000000000000000000000016605200000000000000000000000000000000000000000000000000000000000000167052000000000000000000000000000000000000000000000000000000000000001680520000000000000000000000000000000000000000000000000000000000000016905200000000000000000000000000000000000000000000000000000000000000170052000000000000000000000000000000000000000000000000000000000000001710520000000000000000000000000000000000000000000000000000000000000017205200000000000000000000000000000000000000000000000000000000000000173052000000000000000000000000000000000000000000000000000000000000001740520000000000000000000000000000000000000000000000000000000000000017505200000000000000000000000000000000000000000000000000000000000000176052000000000000000000000000000000000000000000000000000000000000001770520000000000000000000000000000000000000000000000000000000000000017805200000000000000000000000000000000000000000000000000000000000000179052000000000000000000000000000000000000000000000000000000000000001800520000000000000000000000000000000000000000000000000000000000000018105200000000000000000000000000000000000000000000000000000000000000182052000000000000000000000000000000000000000000000000000000000000001830520000000000000000000000000000000000000000000000000000000000000018405200000000000000000000000000000000000000000000000000000000000000185052000000000000000000000000000000000000000000000000000000000000001860520000000000000000000000000000000000000000000000000000000000000018705200000000000000000000000000000000000000000000000000000000000000188052000000000000000000000000000000000000000000000000000000000000001890520000000000000000000000000000000000000000000000000000000000000019005200000000000000000000000000000000000000000000000000000000000000191052000000000000000000000000000000000000000000000000000000000000001920520000000000000000000000000000000000000000000000000000000000000019305200000000000000000000000000000000000000000000000000000000000000194052000000000000000000000000000000000000000000000000000000000000001950520000000000000000000000000000000000000000000000000000000000000019605200000000000000000000000000000000000000000000000000000000000000197052000000000000000000000000000000000000000000000000000000000000001980520000000000000000000000000000000000000000000000000000000000000019905200000000000000000000000000000000000000000000000000000000000000200052000000000000000000000000000000000000000000000000000000000000002010520000000000000000000000000000000000000000000000000000000000000020205200000000000000000000000000000000000000000000000000000000000000203052000000000000000000000000000000000000000000000000000000000000002040520000000000000000000000000000000000000000000000000000000000000020505200000000000000000000000000000000000000000000000000000000000000206052000000000000000000000000000000000000000000000000000000000000002070520000000000000000000000000000000000000000000000000000000000000020805200000000000000000000000000000000000000000000000000000000000000209052000000000000000000000000000000000000000000000000000000000000002100520000000000000000000000000000000000000000000000000000000000000021105200000000000000000000000000000000000000000000000000000000000000212052000000000000000000000000000000000000000000000000000000000000002130520000000000000000000000000000000000000000000000000000000000000021405200000000000000000000000000000000000000000000000000000000000000215052000000000000000000000000000000000000000000000000000000000000002160520000000000000000000000000000000000000000000000000000000000000021705200000000000000000000000000000000000000000000000000000000000000218052000000000000000000000000000000000000000000000000000000000000002190520000000000000000000000000000000000000000000000000000000000000022005200000000000000000000000000000000000000000000000000000000000000221052000000000000000000000000000000000000000000000000000000000000002220520000000000000000000000000000000000000000000000000000000000000022305200000000000000000000000000000000000000000000000000000000000000224052000000000000000000000000000000000000000000000000000000000000002250520000000000000000000000000000000000000000000000000000000000000022605200000000000000000000000000000000000000000000000000000000000000227052000000000000000000000000000000000000000000000000000000000000002280520000000000000000000000000000000000000000000000000000000000000022905200000000000000000000000000000000000000000000000000000000000000230052000000000000000000000000000000000000000000000000000000000000002310520000000000000000000000000000000000000000000000000000000000000023205200000000000000000000000000000000000000000000000000000000000000233052000000000000000000000000000000000000000000000000000000000000002340520000000000000000000000000000000000000000000000000000000000000023505200000000000000000000000000000000000000000000000000000000000000236052000000000000000000000000000000000000000000000000000000000000002370520000000000000000000000000000000000000000000000000000000000000023805200000000000000000000000000000000000000000000000000000000000000239052000000000000000000000000000000000000000000000000000000000000002400520000000000000000000000000000000000000000000000000000000000000024105200000000000000000000000000000000000000000000000000000000000000242052000000000000000000000000000000000000000000000000000000000000002430520000000000000000000000000000000000000000000000000000000000000024405200000000000000000000000000000000000000000000000000000000000000245052000000000000000000000000000000000000000000000000000000000000002460520000000000000000000000000000000000000000000000000000000000000024705200000000000000000000000000000000000000000000000000000000000000248052000000000000000000000000000000000000000000000000000000000000002490520000000000000000000000000000000000000000000000000000000000000025005200000000000000000000000000000000000000000000000000000000000000251052000000000000000000000000000000000000000000000000000000000000002520520000000000000000000000000000000000000000000000000000000000000025305200000000000000000000000000000000000000000000000000000000000000254052000000000000000000000000000000000000000000000000000000000000002550520000000000000000000000000000000000000000000000000000000000000025605200000000000000000000000000000000000000000000000000000000000000257052000000000000000000000000000000000000000000000000000000000000002580520000000000000000000000000000000000000000000000000000000000000025905200000000000000000000000000000000000000000000000000000000000000260052000000000000000000000000000000000000000000000000000000000000002610520000000000000000000000000000000000000000000000000000000000000026205200000000000000000000000000000000000000000000000000000000000000263052000000000000000000000000000000000000000000000000000000000000002640520000000000000000000000000000000000000000000000000000000000000026505200000000000000000000000000000000000000000000000000000000000000266052000000000000000000000000000000000000000000000000000000000000002670520000000000000000000000000000000000000000000000000000000000000026805200000000000000000000000000000000000000000000000000000000000000269052000000000000000000000000000000000000000000000000000000000000002700520000000000000000000000000000000000000000000000000000000000000027105200000000000000000000000000000000000000000000000000000000000000272052000000000000000000000000000000000000000000000000000000000000002730520000000000000000000000000000000000000000000000000000000000000027405200000000000000000000000000000000000000000000000000000000000000275052000000000000000000000000000000000000000000000000000000000000002760520000000000000000000000000000000000000000000000000000000000000027705200000000000000000000000000000000000000000000000000000000000000278052000000000000000000000000000000000000000000000000000000000000002790520000000000000000000000000000000000000000000000000000000000000028005200000000000000000000000000000000000000000000000000000000000000281052000000000000000000000000000000000000000000000000000000000000002820520000000000000000000000000000000000000000000000000000000000000028305200000000000000000000000000000000000000000000000000000000000000284052000000000000000000000000000000000000000000000000000000000000002850520000000000000000000000000000000000000000000000000000000000000028605200000000000000000000000000000000000000000000000000000000000000287052000000000000000000000000000000000000000000000000000000000000002880520000000000000000000000000000000000000000000000000000000000000028905200000000000000000000000000000000000000000000000000000000000000290052000000000000000000000000000000000000000000000000000000000000002910520000000000000000000000000000000000000000000000000000000000000029205200000000000000000000000000000000000000000000000000000000000000293052000000000000000000000000000000000000000000000000000000000000002940520000000000000000000000000000000000000000000000000000000000000029505200000000000000000000000000000000000000000000000000000000000000296052000000000000000000000000000000000000000000000000000000000000002970520000000000000000000000000000000000000000000000000000000000000029805200000000000000000000000000000000000000000000000000000000000000299052000000000000000000000000000000000000000000000000000000000000003000520000000000000000000000000000000000000000000000000000000000000030105200000000000000000000000000000000000000000000000000000000000000302052000000000000000000000000000000000000000000000000000000000000003030520000000000000000000000000000000000000000000000000000000000000030405200000000000000000000000000000000000000000000000000000000000000305052000000000000000000000000000000000000000000000000000000000000003060520000000000000000000000000000000000000000000000000000000000000030705200000000000000000000000000000000000000000000000000000000000000308052000000000000000000000000000000000000000000000000000000000000003090520000000000000000000000000000000000000000000000000000000000000031005200000000000000000000000000000000000000000000000000000000000000311052000000000000000000000000000000000000000000000000000000000000003120520000000000000000000000000000000000000000000000000000000000000031305200000000000000000000000000000000000000000000000000000000000000314052000000000000000000000000000000000000000000000000000000000000003150520000000000000000000000000000000000000000000000000000000000000031605200000000000000000000000000000000000000000000000000000000000000317052000000000000000000000000000000000000000000000000000000000000003180520000000000000000000000000000000000000000000000000000000000000031905200000000000000000000000000000000000000000000000000000000000000320052000000000000000000000000000000000000000000000000000000000000003210520000000000000000000000000000000000000000000000000000000000000032205200000000000000000000000000000000000000000000000000000000000000323052000000000000000000000000000000000000000000000000000000000000003240520000000000000000000000000000000000000000000000000000000000000032505200000000000000000000000000000000000000000000000000000000000000326052000000000000000000000000000000000000000000000000000000000000003270520000000000000000000000000000000000000000000000000000000000000032805200000000000000000000000000000000000000000000000000000000000000329052000000000000000000000000000000000000000000000000000000000000003300520000000000000000000000000000000000000000000000000000000000000033105200000000000000000000000000000000000000000000000000000000000000332052000000000000000000000000000000000000000000000000000000000000003330520000000000000000000000000000000000000000000000000000000000000033405200000000000000000000000000000000000000000000000000000000000000335052000000000000000000000000000000000000000000000000000000000000003360520000000000000000000000000000000000000000000000000000000000000033705200000000000000000000000000000000000000000000000000000000000000338052000000000000000000000000000000000000000000000000000000000000003390520000000000000000000000000000000000000000000000000000000000000034005200000000000000000000000000000000000000000000000000000000000000341052000000000000000000000000000000000000000000000000000000000000003420520000000000000000000000000000000000000000000000000000000000000034305200000000000000000000000000000000000000000000000000000000000000344052000000000000000000000000000000000000000000000000000000000000003450520000000000000000000000000000000000000000000000000000000000000034605200000000000000000000000000000000000000000000000000000000000000347052000000000000000000000000000000000000000000000000000000000000003480520000000000000000000000000000000000000000000000000000000000000034905200000000000000000000000000000000000000000000000000000000000000350052000000000000000000000000000000000000000000000000000000000000003510520000000000000000000000000000000000000000000000000000000000000035205200000000000000000000000000000000000000000000000000000000000000353052000000000000000000000000000000000000000000000000000000000000003540520000000000000000000000000000000000000000000000000000000000000035505200000000000000000000000000000000000000000000000000000000000000356052000000000000000000000000000000000000000000000000000000000000003570520000000000000000000000000000000000000000000000000000000000000035805200000000000000000000000000000000000000000000000000000000000000359052000000000000000000000000000000000000000000000000000000000000003600520000000000000000000000000000000000000000000000000000000000000036105200000000000000000000000000000000000000000000000000000000000000362052000000000000000000000000000000000000000000000000000000000000003630520000000000000000000000000000000000000000000000000000000000000036405200000000000000000000000000000000000000000000000000000000000000365052000000000000000000000000000000000000000000000000000000000000003660520000000000000000000000000000000000000000000000000000000000000036705200000000000000000000000000000000000000000000000000000000000000368052000000000000000000000000000000000000000000000000000000000000003690520000000000000000000000000000000000000000000000000000000000000037005200000000000000000000000000000000000000000000000000000000000000371052000000000000000000000000000000000000000000000000000000000000003720520000000000000000000000000000000000000000000000000000000000000037305200000000000000000000000000000000000000000000000000000000000000374052000000000000000000000000000000000000000000000000000000000000003750520000000000000000000000000000000000000000000000000000000000000037605200000000000000000000000000000000000000000000000000000000000000377052000000000000000000000000000000000000000000000000000000000000003780520000000000000000000000000000000000000000000000000000000000000037905200000000000000000000000000000000000000000000000000000000000000380052000000000000000000000000000000000000000000000000000000000000003810520000000000000000000000000000000000000000000000000000000000000038205200000000000000000000000000000000000000000000000000000000000000383052000000000000000000000000000000000000000000000000000000000000003840520000000000000000000000000000000000000000000000000000000000000038505200000000000000000000000000000000000000000000000000000000000000386052000000000000000000000000000000000000000000000000000000000000003870520000000000000000000000000000000000000000000000000000000000000038805200000000000000000000000000000000000000000000000000000000000000389052000000000000000000000000000000000000000000000000000000000000003900520000000000000000000000000000000000000000000000000000000000000039105200000000000000000000000000000000000000000000000000000000000000392052000000000000000000000000000000000000000000000000000000000000003930520000000000000000000000000000000000000000000000000000000000000039405200000000000000000000000000000000000000000000000000000000000000395052000000000000000000000000000000000000000000000000000000000000003960520000000000000000000000000000000000000000000000000000000000000039705200000000000000000000000000000000000000000000000000000000000000398052000000000000000000000000000000000000000000000000000000000000003990520000000000000000000000000000000000000000000000000000000000000040005200000000000000000000000000000000000000000000000000000000000000401052000000000000000000000000000000000000000000000000000000000000004020520000000000000000000000000000000000000000000000000000000000000040305200000000000000000000000000000000000000000000000000000000000000404052000000000000000000000000000000000000000000000000000000000000004050520000000000000000000000000000000000000000000000000000000000000040605200000000000000000000000000000000000000000000000000000000000000407052000000000000000000000000000000000000000000000000000000000000004080520000000000000000000000000000000000000000000000000000000000000040905200000000000000000000000000000000000000000000000000000000000000410052000000000000000000000000000000000000000000000000000000000000004110520000000000000000000000000000000000000000000000000000000000000041205200000000000000000000000000000000000000000000000000000000000000413052000000000000000000000000000000000000000000000000000000000000004140520000000000000000000000000000000000000000000000000000000000000041505200000000000000000000000000000000000000000000000000000000000000416052000000000000000000000000000000000000000000000000000000000000004170520000000000000000000000000000000000000000000000000000000000000041805200000000000000000000000000000000000000000000000000000000000000419052000000000000000000000000000000000000000000000000000000000000004200520000000000000000000000000000000000000000000000000000000000000042105200000000000000000000000000000000000000000000000000000000000000422052000000000000000000000000000000000000000000000000000000000000004230520000000000000000000000000000000000000000000000000000000000000042405200000000000000000000000000000000000000000000000000000000000000425052000000000000000000000000000000000000000000000000000000000000004260520000000000000000000000000000000000000000000000000000000000000042705200000000000000000000000000000000000000000000000000000000000000428052000000000000000000000000000000000000000000000000000000000000004290520000000000000000000000000000000000000000000000000000000000000043005200000000000000000000000000000000000000000000000000000000000000431052000000000000000000000000000000000000000000000000000000000000004320520000000000000000000000000000000000000000000000000000000000000043305200000000000000000000000000000000000000000000000000000000000000434052000000000000000000000000000000000000000000000000000000000000004350520000000000000000000000000000000000000000000000000000000000000043605200000000000000000000000000000000000000000000000000000000000000437052000000000000000000000000000000000000000000000000000000000000004380520000000000000000000000000000000000000000000000000000000000000043905200000000000000000000000000000000000000000000000000000000000000440052000000000000000000000000000000000000000000000000000000000000004410520000000000000000000000000000000000000000000000000000000000000044205200000000000000000000000000000000000000000000000000000000000000443052000000000000000000000000000000000000000000000000000000000000004440520000000000000000000000000000000000000000000000000000000000000044505200000000000000000000000000000000000000000000000000000000000000446052000000000000000000000000000000000000000000000000000000000000004470520000000000000000000000000000000000000000000000000000000000000044805200000000000000000000000000000000000000000000000000000000000000449052000000000000000000000000000000000000000000000000000000000000004500520000000000000000000000000000000000000000000000000000000000000045105200000000000000000000000000000000000000000000000000000000000000452052000000000000000000000000000000000000000000000000000000000000004530520000000000000000000000000000000000000000000000000000000000000045405200000000000000000000000000000000000000000000000000000000000000455052000000000000000000000000000000000000000000000000000000000000004560520000000000000000000000000000000000000000000000000000000000000045705200000000000000000000000000000000000000000000000000000000000000458052000000000000000000000000000000000000000000000000000000000000004590520000000000000000000000000000000000000000000000000000000000000046005200000000000000000000000000000000000000000000000000000000000000461052000000000000000000000000000000000000000000000000000000000000004620520000000000000000000000000000000000000000000000000000000000000046305200000000000000000000000000000000000000000000000000000000000000464052000000000000000000000000000000000000000000000000000000000000004650520000000000000000000000000000000000000000000000000000000000000046605200000000000000000000000000000000000000000000000000000000000000467052000000000000000000000000000000000000000000000000000000000000004680520000000000000000000000000000000000000000000000000000000000000046905200000000000000000000000000000000000000000000000000000000000000470052000000000000000000000000000000000000000000000000000000000000004710520000000000000000000000000000000000000000000000000000000000000047205200000000000000000000000000000000000000000000000000000000000000473052000000000000000000000000000000000000000000000000000000000000004740520000000000000000000000000000000000000000000000000000000000000047505200000000000000000000000000000000000000000000000000000000000000476052000000000000000000000000000000000000000000000000000000000000004770520000000000000000000000000000000000000000000000000000000000000047805200000000000000000000000000000000000000000000000000000000000000479052000000000000000000000000000000000000000000000000000000000000004800520000000000000000000000000000000000000000000000000000000000000048105200000000000000000000000000000000000000000000000000000000000000482052000000000000000000000000000000000000000000000000000000000000004830520000000000000000000000000000000000000000000000000000000000000048405200000000000000000000000000000000000000000000000000000000000000485052000000000000000000000000000000000000000000000000000000000000004860520000000000000000000000000000000000000000000000000000000000000048705200000000000000000000000000000000000000000000000000000000000000488052000000000000000000000000000000000000000000000000000000000000004890520000000000000000000000000000000000000000000000000000000000000049005200000000000000000000000000000000000000000000000000000000000000491052000000000000000000000000000000000000000000000000000000000000004920520000000000000000000000000000000000000000000000000000000000000049305200000000000000000000000000000000000000000000000000000000000000494052000000000000000000000000000000000000000000000000000000000000004950520000000000000000000000000000000000000000000000000000000000000049605200000000000000000000000000000000000000000000000000000000000000497052000000000000000000000000000000000000000000000000000000000000004980520000000000000000000000000000000000000000000000000000000000000049905200000000000000000000000000000000000000000000000000000000000000500052000000000000000000000000000000000000000000000000000000000000005010520000000000000000000000000000000000000000000000000000000000000050205200000000000000000000000000000000000000000000000000000000000000503052000000000000000000000000000000000000000000000000000000000000005040520000000000000000000000000000000000000000000000000000000000000050505200000000000000000000000000000000000000000000000000000000000000506052000000000000000000000000000000000000000000000000000000000000005070520000000000000000000000000000000000000000000000000000000000000050805200000000000000000000000000000000000000000000000000000000000000509052000000000000000000000000000000000000000000000000000000000000005100520000000000000000000000000000000000000000000000000000000000000051105200000000000000000000000000000000000000000000000000000000000000512052000000000000000000000000000000000000000000000000000000000000005130520000000000000000000000000000000000000000000000000000000000000051405200000000000000000000000000000000000000000000000000000000000000515052000000000000000000000000000000000000000000000000000000000000005160520000000000000000000000000000000000000000000000000000000000000051705200000000000000000000000000000000000000000000000000000000000000518052000000000000000000000000000000000000000000000000000000000000005190520000000000000000000000000000000000000000000000000000000000000052005200000000000000000000000000000000000000000000000000000000000000521052000000000000000000000000000000000000000000000000000000000000005220520000000000000000000000000000000000000000000000000000000000000052305200000000000000000000000000000000000000000000000000000000000000524052000000000000000000000000000000000000000000000000000000000000005250520000000000000000000000000000000000000000000000000000000000000052605200000000000000000000000000000000000000000000000000000000000000527052000000000000000000000000000000000000000000000000000000000000005280520000000000000000000000000000000000000000000000000000000000000052905200000000000000000000000000000000000000000000000000000000000000530052000000000000000000000000000000000000000000000000000000000000005310520000000000000000000000000000000000000000000000000000000000000053205200000000000000000000000000000000000000000000000000000000000000533052000000000000000000000000000000000000000000000000000000000000005340520000000000000000000000000000000000000000000000000000000000000053505200000000000000000000000000000000000000000000000000000000000000536052000000000000000000000000000000000000000000000000000000000000005370520000000000000000000000000000000000000000000000000000000000000053805200000000000000000000000000000000000000000000000000000000000000539052000000000000000000000000000000000000000000000000000000000000005400520000000000000000000000000000000000000000000000000000000000000054105200000000000000000000000000000000000000000000000000000000000000542052000000000000000000000000000000000000000000000000000000000000005430520000000000000000000000000000000000000000000000000000000000000054405200000000000000000000000000000000000000000000000000000000000000545052000000000000000000000000000000000000000000000000000000000000005460520000000000000000000000000000000000000000000000000000000000000054705200000000000000000000000000000000000000000000000000000000000000548052000000000000000000000000000000000000000000000000000000000000005490520000000000000000000000000000000000000000000000000000000000000055005200000000000000000000000000000000000000000000000000000000000000551052000000000000000000000000000000000000000000000000000000000000005520520000000000000000000000000000000000000000000000000000000000000055305200000000000000000000000000000000000000000000000000000000000000554052000000000000000000000000000000000000000000000000000000000000005550520000000000000000000000000000000000000000000000000000000000000055605200000000000000000000000000000000000000000000000000000000000000557052000000000000000000000000000000000000000000000000000000000000005580520000000000000000000000000000000000000000000000000000000000000055905200000000000000000000000000000000000000000000000000000000000000560052000000000000000000000000000000000000000000000000000000000000005610520000000000000000000000000000000000000000000000000000000000000056205200000000000000000000000000000000000000000000000000000000000000563052000000000000000000000000000000000000000000000000000000000000005640520000000000000000000000000000000000000000000000000000000000000056505200000000000000000000000000000000000000000000000000000000000000566052000000000000000000000000000000000000000000000000000000000000005670520000000000000000000000000000000000000000000000000000000000000056805200000000000000000000000000000000000000000000000000000000000000569052000000000000000000000000000000000000000000000000000000000000005700520000000000000000000000000000000000000000000000000000000000000057105200000000000000000000000000000000000000000000000000000000000000572052000000000000000000000000000000000000000000000000000000000000005730520000000000000000000000000000000000000000000000000000000000000057405200000000000000000000000000000000000000000000000000000000000000575052000000000000000000000000000000000000000000000000000000000000005760520000000000000000000000000000000000000000000000000000000000000057705200000000000000000000000000000000000000000000000000000000000000578052000000000000000000000000000000000000000000000000000000000000005790520000000000000000000000000000000000000000000000000000000000000058005200000000000000000000000000000000000000000000000000000000000000581052000000000000000000000000000000000000000000000000000000000000005820520000000000000000000000000000000000000000000000000000000000000058305200000000000000000000000000000000000000000000000000000000000000584052000000000000000000000000000000000000000000000000000000000000005850520000000000000000000000000000000000000000000000000000000000000058605200000000000000000000000000000000000000000000000000000000000000587052000000000000000000000000000000000000000000000000000000000000005880520000000000000000000000000000000000000000000000000000000000000058905200000000000000000000000000000000000000000000000000000000000000590052000000000000000000000000000000000000000000000000000000000000005910520000000000000000000000000000000000000000000000000000000000000059205200000000000000000000000000000000000000000000000000000000000000593052000000000000000000000000000000000000000000000000000000000000005940520000000000000000000000000000000000000000000000000000000000000059505200000000000000000000000000000000000000000000000000000000000000596052000000000000000000000000000000000000000000000000000000000000005970520000000000000000000000000000000000000000000000000000000000000059805200000000000000000000000000000000000000000000000000000000000000599052000000000000000000000000000000000000000000000000000000000000006000520000000000000000000000000000000000000000000000000000000000000060105200000000000000000000000000000000000000000000000000000000000000602052000000000000000000000000000000000000000000000000000000000000006030520000000000000000000000000000000000000000000000000000000000000060405200000000000000000000000000000000000000000000000000000000000000605052000000000000000000000000000000000000000000000000000000000000006060520000000000000000000000000000000000000000000000000000000000000060705200000000000000000000000000000000000000000000000000000000000000608052000000000000000000000000000000000000000000000000000000000000006090520000000000000000000000000000000000000000000000000000000000000061005200000000000000000000000000000000000000000000000000000000000000611052000000000000000000000000000000000000000000000000000000000000006120520000000000000000000000000000000000000000000000000000000000000061305200000000000000000000000000000000000000000000000000000000000000614052000000000000000000000000000000000000000000000000000000000000006150520000000000000000000000000000000000000000000000000000000000000061605200000000000000000000000000000000000000000000000000000000000000617052000000000000000000000000000000000000000000000000000000000000006180520000000000000000000000000000000000000000000000000000000000000061905200000000000000000000000000000000000000000000000000000000000000620052000000000000000000000000000000000000000000000000000000000000006210520000000000000000000000000000000000000000000000000000000000000062205200000000000000000000000000000000000000000000000000000000000000623052000000000000000000000000000000000000000000000000000000000000006240520000000000000000000000000000000000000000000000000000000000000062505200000000000000000000000000000000000000000000000000000000000000626052000000000000000000000000000000000000000000000000000000000000006270520000000000000000000000000000000000000000000000000000000000000062805200000000000000000000000000000000000000000000000000000000000000629052000000000000000000000000000000000000000000000000000000000000006300520000000000000000000000000000000000000000000000000000000000000063105200000000000000000000000000000000000000000000000000000000000000632052000000000000000000000000000000000000000000000000000000000000006330520000000000000000000000000000000000000000000000000000000000000063405200000000000000000000000000000000000000000000000000000000000000635052000000000000000000000000000000000000000000000000000000000000006360520000000000000000000000000000000000000000000000000000000000000063705200000000000000000000000000000000000000000000000000000000000000638052000000000000000000000000000000000000000000000000000000000000006390520000000000000000000000000000000000000000000000000000000000000064005200000000000000000000000000000000000000000000000000000000000000641052000000000000000000000000000000000000000000000000000000000000006420520000000000000000000000000000000000000000000000000000000000000064305200000000000000000000000000000000000000000000000000000000000000644052000000000000000000000000000000000000000000000000000000000000006450520000000000000000000000000000000000000000000000000000000000000064605200000000000000000000000000000000000000000000000000000000000000647052000000000000000000000000000000000000000000000000000000000000006480520000000000000000000000000000000000000000000000000000000000000064905200000000000000000000000000000000000000000000000000000000000000650052000000000000000000000000000000000000000000000000000000000000006510520000000000000000000000000000000000000000000000000000000000000065205200000000000000000000000000000000000000000000000000000000000000653052000000000000000000000000000000000000000000000000000000000000006540520000000000000000000000000000000000000000000000000000000000000065505200000000000000000000000000000000000000000000000000000000000000656052000000000000000000000000000000000000000000000000000000000000006570520000000000000000000000000000000000000000000000000000000000000065805200000000000000000000000000000000000000000000000000000000000000659052000000000000000000000000000000000000000000000000000000000000006600520000000000000000000000000000000000000000000000000000000000000066105200000000000000000000000000000000000000000000000000000000000000662052000000000000000000000000000000000000000000000000000000000000006630520000000000000000000000000000000000000000000000000000000000000066405200000000000000000000000000000000000000000000000000000000000000665052000000000000000000000000000000000000000000000000000000000000006660520000000000000000000000000000000000000000000000000000000000000066705200000000000000000000000000000000000000000000000000000000000000668052000000000000000000000000000000000000000000000000000000000000006690520000000000000000000000000000000000000000000000000000000000000067005200000000000000000000000000000000000000000000000000000000000000671052000000000000000000000000000000000000000000000000000000000000006720520000000000000000000000000000000000000000000000000000000000000067305200000000000000000000000000000000000000000000000000000000000000674052000000000000000000000000000000000000000000000000000000000000006750520000000000000000000000000000000000000000000000000000000000000067605200000000000000000000000000000000000000000000000000000000000000677052000000000000000000000000000000000000000000000000000000000000006780520000000000000000000000000000000000000000000000000000000000000067905200000000000000000000000000000000000000000000000000000000000000680052000000000000000000000000000000000000000000000000000000000000006810520000000000000000000000000000000000000000000000000000000000000068205200000000000000000000000000000000000000000000000000000000000000683052000000000000000000000000000000000000000000000000000000000000006840520000000000000000000000000000000000000000000000000000000000000068505200000000000000000000000000000000000000000000000000000000000000686052000000000000000000000000000000000000000000000000000000000000006870520000000000000000000000000000000000000000000000000000000000000068805200000000000000000000000000000000000000000000000000000000000000689052000000000000000000000000000000000000000000000000000000000000006900520000000000000000000000000000000000000000000000000000000000000069105200000000000000000000000000000000000000000000000000000000000000692052000000000000000000000000000000000000000000000000000000000000006930520000000000000000000000000000000000000000000000000000000000000069405200000000000000000000000000000000000000000000000000000000000000695052000000000000000000000000000000000000000000000000000000000000006960520000000000000000000000000000000000000000000000000000000000000069705200000000000000000000000000000000000000000000000000000000000000698052000000000000000000000000000000000000000000000000000000000000006990520000000000000000000000000000000000000000000000000000000000000070005200000000000000000000000000000000000000000000000000000000000000701052000000000000000000000000000000000000000000000000000000000000007020520000000000000000000000000000000000000000000000000000000000000070305200000000000000000000000000000000000000000000000000000000000000704052000000000000000000000000000000000000000000000000000000000000007050520000000000000000000000000000000000000000000000000000000000000070605200000000000000000000000000000000000000000000000000000000000000707052000000000000000000000000000000000000000000000000000000000000007080520000000000000000000000000000000000000000000000000000000000000070905200000000000000000000000000000000000000000000000000000000000000710052000000000000000000000000000000000000000000000000000000000000007110520000000000000000000000000000000000000000000000000000000000000071205200000000000000000000000000000000000000000000000000000000000000713052000000000000000000000000000000000000000000000000000000000000007140520000000000000000000000000000000000000000000000000000000000000071505200000000000000000000000000000000000000000000000000000000000000716052000000000000000000000000000000000000000000000000000000000000007170520000000000000000000000000000000000000000000000000000000000000071805200000000000000000000000000000000000000000000000000000000000000719052000000000000000000000000000000000000000000000000000000000000007200520000000000000000000000000000000000000000000000000000000000000072105200000000000000000000000000000000000000000000000000000000000000722052000000000000000000000000000000000000000000000000000000000000007230520000000000000000000000000000000000000000000000000000000000000072405200000000000000000000000000000000000000000000000000000000000000725052000000000000000000000000000000000000000000000000000000000000007260520000000000000000000000000000000000000000000000000000000000000072705200000000000000000000000000000000000000000000000000000000000000728052000000000000000000000000000000000000000000000000000000000000007290520000000000000000000000000000000000000000000000000000000000000073005200000000000000000000000000000000000000000000000000000000000000731052000000000000000000000000000000000000000000000000000000000000007320520000000000000000000000000000000000000000000000000000000000000073305200000000000000000000000000000000000000000000000000000000000000734052000000000000000000000000000000000000000000000000000000000000007350520000000000000000000000000000000000000000000000000000000000000073605200000000000000000000000000000000000000000000000000000000000000737052000000000000000000000000000000000000000000000000000000000000007380520000000000000000000000000000000000000000000000000000000000000073905200000000000000000000000000000000000000000000000000000000000000740052000000000000000000000000000000000000000000000000000000000000007410520000000000000000000000000000000000000000000000000000000000000074205200000000000000000000000000000000000000000000000000000000000000743052000000000000000000000000000000000000000000000000000000000000007440520000000000000000000000000000000000000000000000000000000000000074505200000000000000000000000000000000000000000000000000000000000000746052000000000000000000000000000000000000000000000000000000000000007470520000000000000000000000000000000000000000000000000000000000000074805200000000000000000000000000000000000000000000000000000000000000749052000000000000000000000000000000000000000000000000000000000000007500520000000000000000000000000000000000000000000000000000000000000075105200000000000000000000000000000000000000000000000000000000000000752052000000000000000000000000000000000000000000000000000000000000007530520000000000000000000000000000000000000000000000000000000000000075405200000000000000000000000000000000000000000000000000000000000000755052000000000000000000000000000000000000000000000000000000000000007560520000000000000000000000000000000000000000000000000000000000000075705200000000000000000000000000000000000000000000000000000000000000758052000000000000000000000000000000000000000000000000000000000000007590520000000000000000000000000000000000000000000000000000000000000076005200000000000000000000000000000000000000000000000000000000000000761052000000000000000000000000000000000000000000000000000000000000007620520000000000000000000000000000000000000000000000000000000000000076305200000000000000000000000000000000000000000000000000000000000000764052000000000000000000000000000000000000000000000000000000000000007650520000000000000000000000000000000000000000000000000000000000000076605200000000000000000000000000000000000000000000000000000000000000767052000000000000000000000000000000000000000000000000000000000000007680520000000000000000000000000000000000000000000000000000000000000076905200000000000000000000000000000000000000000000000000000000000000770052000000000000000000000000000000000000000000000000000000000000007710520000000000000000000000000000000000000000000000000000000000000077205200000000000000000000000000000000000000000000000000000000000000773052000000000000000000000000000000000000000000000000000000000000007740520000000000000000000000000000000000000000000000000000000000000077505200000000000000000000000000000000000000000000000000000000000000776052000000000000000000000000000000000000000000000000000000000000007770520000000000000000000000000000000000000000000000000000000000000077805200000000000000000000000000000000000000000000000000000000000000779052000000000000000000000000000000000000000000000000000000000000007800520000000000000000000000000000000000000000000000000000000000000078105200000000000000000000000000000000000000000000000000000000000000782052000000000000000000000000000000000000000000000000000000000000007830520000000000000000000000000000000000000000000000000000000000000078405200000000000000000000000000000000000000000000000000000000000000785052000000000000000000000000000000000000000000000000000000000000007860520000000000000000000000000000000000000000000000000000000000000078705200000000000000000000000000000000000000000000000000000000000000788052000000000000000000000000000000000000000000000000000000000000007890520000000000000000000000000000000000000000000000000000000000000079005200000000000000000000000000000000000000000000000000000000000000791052000000000000000000000000000000000000000000000000000000000000007920520000000000000000000000000000000000000000000000000000000000000079305200000000000000000000000000000000000000000000000000000000000000794052000000000000000000000000000000000000000000000000000000000000007950520000000000000000000000000000000000000000000000000000000000000079605200000000000000000000000000000000000000000000000000000000000000797052000000000000000000000000000000000000000000000000000000000000007980520000000000000000000000000000000000000000000000000000000000000079905200000000000000000000000000000000000000000000000000000000000000800052000000000000000000000000000000000000000000000000000000000000008010520000000000000000000000000000000000000000000000000000000000000080205200000000000000000000000000000000000000000000000000000000000000803052000000000000000000000000000000000000000000000000000000000000008040520000000000000000000000000000000000000000000000000000000000000080505200000000000000000000000000000000000000000000000000000000000000806052000000000000000000000000000000000000000000000000000000000000008070520000000000000000000000000000000000000000000000000000000000000080805200000000000000000000000000000000000000000000000000000000000000809052000000000000000000000000000000000000000000000000000000000000008100520000000000000000000000000000000000000000000000000000000000000081105200000000000000000000000000000000000000000000000000000000000000812052000000000000000000000000000000000000000000000000000000000000008130520000000000000000000000000000000000000000000000000000000000000081405200000000000000000000000000000000000000000000000000000000000000815052000000000000000000000000000000000000000000000000000000000000008160520000000000000000000000000000000000000000000000000000000000000081705200000000000000000000000000000000000000000000000000000000000000818052000000000000000000000000000000000000000000000000000000000000008190520000000000000000000000000000000000000000000000000000000000000082005200000000000000000000000000000000000000000000000000000000000000821052000000000000000000000000000000000000000000000000000000000000008220520000000000000000000000000000000000000000000000000000000000000082305200000000000000000000000000000000000000000000000000000000000000824052000000000000000000000000000000000000000000000000000000000000008250520000000000000000000000000000000000000000000000000000000000000082605200000000000000000000000000000000000000000000000000000000000000827052000000000000000000000000000000000000000000000000000000000000008280520000000000000000000000000000000000000000000000000000000000000082905200000000000000000000000000000000000000000000000000000000000000830052000000000000000000000000000000000000000000000000000000000000008310520000000000000000000000000000000000000000000000000000000000000083205200000000000000000000000000000000000000000000000000000000000000833052000000000000000000000000000000000000000000000000000000000000008340520000000000000000000000000000000000000000000000000000000000000083505200000000000000000000000000000000000000000000000000000000000000836052000000000000000000000000000000000000000000000000000000000000008370520000000000000000000000000000000000000000000000000000000000000083805200000000000000000000000000000000000000000000000000000000000000839052000000000000000000000000000000000000000000000000000000000000008400520000000000000000000000000000000000000000000000000000000000000084105200000000000000000000000000000000000000000000000000000000000000842052000000000000000000000000000000000000000000000000000000000000008430520000000000000000000000000000000000000000000000000000000000000084405200000000000000000000000000000000000000000000000000000000000000845052000000000000000000000000000000000000000000000000000000000000008460520000000000000000000000000000000000000000000000000000000000000084705200000000000000000000000000000000000000000000000000000000000000848052000000000000000000000000000000000000000000000000000000000000008490520000000000000000000000000000000000000000000000000000000000000085005200000000000000000000000000000000000000000000000000000000000000851052000000000000000000000000000000000000000000000000000000000000008520520000000000000000000000000000000000000000000000000000000000000085305200000000000000000000000000000000000000000000000000000000000000854052000000000000000000000000000000000000000000000000000000000000008550520000000000000000000000000000000000000000000000000000000000000085605200000000000000000000000000000000000000000000000000000000000000857052000000000000000000000000000000000000000000000000000000000000008580520000000000000000000000000000000000000000000000000000000000000085905200000000000000000000000000000000000000000000000000000000000000860052000000000000000000000000000000000000000000000000000000000000008610520000000000000000000000000000000000000000000000000000000000000086205200000000000000000000000000000000000000000000000000000000000000863052000000000000000000000000000000000000000000000000000000000000008640520000000000000000000000000000000000000000000000000000000000000086505200000000000000000000000000000000000000000000000000000000000000866052000000000000000000000000000000000000000000000000000000000000008670520000000000000000000000000000000000000000000000000000000000000086805200000000000000000000000000000000000000000000000000000000000000869052000000000000000000000000000000000000000000000000000000000000008700520000000000000000000000000000000000000000000000000000000000000087105200000000000000000000000000000000000000000000000000000000000000872052000000000000000000000000000000000000000000000000000000000000008730520000000000000000000000000000000000000000000000000000000000000087405200000000000000000000000000000000000000000000000000000000000000875052000000000000000000000000000000000000000000000000000000000000008760520000000000000000000000000000000000000000000000000000000000000087705200000000000000000000000000000000000000000000000000000000000000878052000000000000000000000000000000000000000000000000000000000000008790520000000000000000000000000000000000000000000000000000000000000088005200000000000000000000000000000000000000000000000000000000000000881052000000000000000000000000000000000000000000000000000000000000008820520000000000000000000000000000000000000000000000000000000000000088305200000000000000000000000000000000000000000000000000000000000000884052000000000000000000000000000000000000000000000000000000000000008850520000000000000000000000000000000000000000000000000000000000000088605200000000000000000000000000000000000000000000000000000000000000887052000000000000000000000000000000000000000000000000000000000000008880520000000000000000000000000000000000000000000000000000000000000088905200000000000000000000000000000000000000000000000000000000000000890052000000000000000000000000000000000000000000000000000000000000008910520000000000000000000000000000000000000000000000000000000000000089205200000000000000000000000000000000000000000000000000000000000000893052000000000000000000000000000000000000000000000000000000000000008940520000000000000000000000000000000000000000000000000000000000000089505200000000000000000000000000000000000000000000000000000000000000896052000000000000000000000000000000000000000000000000000000000000008970520000000000000000000000000000000000000000000000000000000000000089805200000000000000000000000000000000000000000000000000000000000000899052000000000000000000000000000000000000000000000000000000000000009000520000000000000000000000000000000000000000000000000000000000000090105200000000000000000000000000000000000000000000000000000000000000902052000000000000000000000000000000000000000000000000000000000000009030520000000000000000000000000000000000000000000000000000000000000090405200000000000000000000000000000000000000000000000000000000000000905052000000000000000000000000000000000000000000000000000000000000009060520000000000000000000000000000000000000000000000000000000000000090705200000000000000000000000000000000000000000000000000000000000000908052000000000000000000000000000000000000000000000000000000000000009090520000000000000000000000000000000000000000000000000000000000000091005200000000000000000000000000000000000000000000000000000000000000911052000000000000000000000000000000000000000000000000000000000000009120520000000000000000000000000000000000000000000000000000000000000091305200000000000000000000000000000000000000000000000000000000000000914052000000000000000000000000000000000000000000000000000000000000009150520000000000000000000000000000000000000000000000000000000000000091605200000000000000000000000000000000000000000000000000000000000000917052000000000000000000000000000000000000000000000000000000000000009180520000000000000000000000000000000000000000000000000000000000000091905200000000000000000000000000000000000000000000000000000000000000920052000000000000000000000000000000000000000000000000000000000000009210520000000000000000000000000000000000000000000000000000000000000092205200000000000000000000000000000000000000000000000000000000000000923052000000000000000000000000000000000000000000000000000000000000009240520000000000000000000000000000000000000000000000000000000000000092505200000000000000000000000000000000000000000000000000000000000000926052000000000000000000000000000000000000000000000000000000000000009270520000000000000000000000000000000000000000000000000000000000000092805200000000000000000000000000000000000000000000000000000000000000929052000000000000000000000000000000000000000000000000000000000000009300520000000000000000000000000000000000000000000000000000000000000093105200000000000000000000000000000000000000000000000000000000000000932052000000000000000000000000000000000000000000000000000000000000009330520000000000000000000000000000000000000000000000000000000000000093405200000000000000000000000000000000000000000000000000000000000000935052000000000000000000000000000000000000000000000000000000000000009360520000000000000000000000000000000000000000000000000000000000000093705200000000000000000000000000000000000000000000000000000000000000938052000000000000000000000000000000000000000000000000000000000000009390520000000000000000000000000000000000000000000000000000000000000094005200000000000000000000000000000000000000000000000000000000000000941052000000000000000000000000000000000000000000000000000000000000009420520000000000000000000000000000000000000000000000000000000000000094305200000000000000000000000000000000000000000000000000000000000000944052000000000000000000000000000000000000000000000000000000000000009450520000000000000000000000000000000000000000000000000000000000000094605200000000000000000000000000000000000000000000000000000000000000947052000000000000000000000000000000000000000000000000000000000000009480520000000000000000000000000000000000000000000000000000000000000094905200000000000000000000000000000000000000000000000000000000000000950052000000000000000000000000000000000000000000000000000000000000009510520000000000000000000000000000000000000000000000000000000000000095205200000000000000000000000000000000000000000000000000000000000000953052000000000000000000000000000000000000000000000000000000000000009540520000000000000000000000000000000000000000000000000000000000000095505200000000000000000000000000000000000000000000000000000000000000956052000000000000000000000000000000000000000000000000000000000000009570520000000000000000000000000000000000000000000000000000000000000095805200000000000000000000000000000000000000000000000000000000000000959052000000000000000000000000000000000000000000000000000000000000009600520000000000000000000000000000000000000000000000000000000000000096105200000000000000000000000000000000000000000000000000000000000000962052000000000000000000000000000000000000000000000000000000000000009630520000000000000000000000000000000000000000000000000000000000000096405200000000000000000000000000000000000000000000000000000000000000965052000000000000000000000000000000000000000000000000000000000000009660520000000000000000000000000000000000000000000000000000000000000096705200000000000000000000000000000000000000000000000000000000000000968052000000000000000000000000000000000000000000000000000000000000009690520000000000000000000000000000000000000000000000000000000000000097005200000000000000000000000000000000000000000000000000000000000000971052000000000000000000000000000000000000000000000000000000000000009720520000000000000000000000000000000000000000000000000000000000000097305200000000000000000000000000000000000000000000000000000000000000974052000000000000000000000000000000000000000000000000000000000000009750520000000000000000000000000000000000000000000000000000000000000097605200000000000000000000000000000000000000000000000000000000000000977052000000000000000000000000000000000000000000000000000000000000009780520000000000000000000000000000000000000000000000000000000000000097905200000000000000000000000000000000000000000000000000000000000000980052000000000000000000000000000000000000000000000000000000000000009810520000000000000000000000000000000000000000000000000000000000000098205200000000000000000000000000000000000000000000000000000000000000983052000000000000000000000000000000000000000000000000000000000000009840520000000000000000000000000000000000000000000000000000000000000098505200000000000000000000000000000000000000000000000000000000000000986052000000000000000000000000000000000000000000000000000000000000009870520000000000000000000000000000000000000000000000000000000000000098805200000000000000000000000000000000000000000000000000000000000000989052000000000000000000000000000000000000000000000000000000000000009900520000000000000000000000000000000000000000000000000000000000000099105200000000000000000000000000000000000000000000000000000000000000992052000000000000000000000000000000000000000000000000000000000000009930520000000000000000000000000000000000000000000000000000000000000099405200000000000000000000000000000000000000000000000000000000000000995052000000000000000000000000000000000000000000000000000000000000009960520000000000000000000000000000000000000000000000000000000000000099705200000000000000000000000000000000000000000000000000000000000000998052000000000000000000000000000000000000000000000000000000000000009990520e57eb8d10b6eed36a88a464538f8fcedbba7b1a71d6ed52124f77b835807d0fd05201c53ca351e7f26f963ced56cf18f9ebee56535170171b72a0addb4e1613776850520636b8e5a599cfe0833dbe141dbc093d65e0a497451ac21415fbf03698587c7b905204ea21cad94a85adb252b0a76b9826c67d7c697ca386d201752e38f19ed92a223126170746f733a3a6d657461646174615f7630c201032003000000000000134552525f414c52454144595f50524553454e542c4164647265737320697320616c72656164792070726573656e7420696e207468652077686974656c6973742e2103000000000000134552525f4e4f5f535543485f41444452455353214e6f2073756368206164647265737320696e207468652077686974656c6973742e2203000000000000134552525f4e4f545f494e495449414c495a45441d57686974656c697374206973206e6f7420696e697469616c697a65642e000201090b010205020001040100051d0b0011050c020b02070321030907012707032900030e0701270a01110220031407002707032a000c030b030f000b013101380002010104010008b01f0b0011050c010b01070321030907012707032a000c030b030f000c020a020704310138000a020705310138000a020706310138000a020707310138000a020708310138000a020709310138000a02070a310138000a02070b310138000a02070c310138000a02070d310138000a02070e310138000a02070f310138000a020710310138000a020711310138000a020712310138000a020713310138000a020714310138000a020715310138000a020716310138000a020717310138000a020718310138000a020719310138000a02071a310138000a02071b310138000a02071c310138000a02071d310138000a02071e310138000a02071f310138000a020720310138000a020721310138000a020722310138000a020723310138000a020724310138000a020725310138000a020726310138000a020727310138000a020728310138000a020729310138000a02072a310138000a02072b310138000a02072c310138000a02072d310138000a02072e310138000a02072f310138000a020730310138000a020731310138000a020732310138000a020733310138000a020734310138000a020735310138000a020736310138000a020737310138000a020738310138000a020739310138000a02073a310138000a02073b310138000a02073c310138000a02073d310138000a02073e310138000a02073f310138000a020740310138000a020741310138000a020742310138000a020743310138000a020744310138000a020745310138000a020746310138000a020747310138000a020748310138000a020749310138000a02074a310138000a02074b310138000a02074c310138000a02074d310138000a02074e310138000a02074f310138000a020750310138000a020751310138000a020752310138000a020753310138000a020754310138000a020755310138000a020756310138000a020757310138000a020758310138000a020759310138000a02075a310138000a02075b310138000a02075c310138000a02075d310138000a02075e310138000a02075f310138000a020760310138000a020761310138000a020762310138000a020763310138000a020764310138000a020765310138000a020766310138000a020767310138000a020768310138000a020769310138000a02076a310138000a02076b310138000a02076c310138000a02076d310138000a02076e310138000a02076f310138000a020770310138000a020771310138000a020772310138000a020773310138000a020774310138000a020775310138000a020776310138000a020777310138000a020778310138000a020779310138000a02077a310138000a02077b310138000a02077c310138000a02077d310138000a02077e310138000a02077f310138000a02078001310138000a02078101310138000a02078201310138000a02078301310138000a02078401310138000a02078501310138000a02078601310138000a02078701310138000a02078801310138000a02078901310138000a02078a01310138000a02078b01310138000a02078c01310138000a02078d01310138000a02078e01310138000a02078f01310138000a02079001310138000a02079101310138000a02079201310138000a02079301310138000a02079401310138000a02079501310138000a02079601310138000a02079701310138000a02079801310138000a02079901310138000a02079a01310138000a02079b01310138000a02079c01310138000a02079d01310138000a02079e01310138000a02079f01310138000a0207a001310138000a0207a101310138000a0207a201310138000a0207a301310138000a0207a401310138000a0207a501310138000a0207a601310138000a0207a701310138000a0207a801310138000a0207a901310138000a0207aa01310138000a0207ab01310138000a0207ac01310138000a0207ad01310138000a0207ae01310138000a0207af01310138000a0207b001310138000a0207b101310138000a0207b201310138000a0207b301310138000a0207b401310138000a0207b501310138000a0207b601310138000a0207b701310138000a0207b801310138000a0207b901310138000a0207ba01310138000a0207bb01310138000a0207bc01310138000a0207bd01310138000a0207be01310138000a0207bf01310138000a0207c001310138000a0207c101310138000a0207c201310138000a0207c301310138000a0207c401310138000a0207c501310138000a0207c601310138000a0207c701310138000a0207c801310138000a0207c901310138000a0207ca01310138000a0207cb01310138000a0207cc01310138000a0207cd01310138000a0207ce01310138000a0207cf01310138000a0207d001310138000a0207d101310138000a0207d201310138000a0207d301310138000a0207d401310138000a0207d501310138000a0207d601310138000a0207d701310138000a0207d801310138000a0207d901310138000a0207da01310138000a0207db01310138000a0207dc01310138000a0207dd01310138000a0207de01310138000a0207df01310138000a0207e001310138000a0207e101310138000a0207e201310138000a0207e301310138000a0207e401310138000a0207e501310138000a0207e601310138000a0207e701310138000a0207e801310138000a0207e901310138000a0207ea01310138000a0207eb01310138000a0207ec01310138000a0207ed01310138000a0207ee01310138000a0207ef01310138000a0207f001310138000a0207f101310138000a0207f201310138000a0207f301310138000a0207f401310138000a0207f501310138000a0207f601310138000a0207f701310138000a0207f801310138000a0207f901310138000a0207fa01310138000a0207fb01310138000a0207fc01310138000a0207fd01310138000a0207fe01310138000a0207ff01310138000a02078002310138000a02078102310138000a02078202310138000a02078302310138000a02078402310138000a02078502310138000a02078602310138000a02078702310138000a02078802310138000a02078902310138000a02078a02310138000a02078b02310138000a02078c02310138000a02078d02310138000a02078e02310138000a02078f02310138000a02079002310138000a02079102310138000a02079202310138000a02079302310138000a02079402310138000a02079502310138000a02079602310138000a02079702310138000a02079802310138000a02079902310138000a02079a02310138000a02079b02310138000a02079c02310138000a02079d02310138000a02079e02310138000a02079f02310138000a0207a002310138000a0207a102310138000a0207a202310138000a0207a302310138000a0207a402310138000a0207a502310138000a0207a602310138000a0207a702310138000a0207a802310138000a0207a902310138000a0207aa02310138000a0207ab02310138000a0207ac02310138000a0207ad02310138000a0207ae02310138000a0207af02310138000a0207b002310138000a0207b102310138000a0207b202310138000a0207b302310138000a0207b402310138000a0207b502310138000a0207b602310138000a0207b702310138000a0207b802310138000a0207b902310138000a0207ba02310138000a0207bb02310138000a0207bc02310138000a0207bd02310138000a0207be02310138000a0207bf02310138000a0207c002310138000a0207c102310138000a0207c202310138000a0207c302310138000a0207c402310138000a0207c502310138000a0207c602310138000a0207c702310138000a0207c802310138000a0207c902310138000a0207ca02310138000a0207cb02310138000a0207cc02310138000a0207cd02310138000a0207ce02310138000a0207cf02310138000a0207d002310138000a0207d102310138000a0207d202310138000a0207d302310138000a0207d402310138000a0207d502310138000a0207d602310138000a0207d702310138000a0207d802310138000a0207d902310138000a0207da02310138000a0207db02310138000a0207dc02310138000a0207dd02310138000a0207de02310138000a0207df02310138000a0207e002310138000a0207e102310138000a0207e202310138000a0207e302310138000a0207e402310138000a0207e502310138000a0207e602310138000a0207e702310138000a0207e802310138000a0207e902310138000a0207ea02310138000a0207eb02310138000a0207ec02310138000a0207ed02310138000a0207ee02310138000a0207ef02310138000a0207f002310138000a0207f102310138000a0207f202310138000a0207f302310138000a0207f402310138000a0207f502310138000a0207f602310138000a0207f702310138000a0207f802310138000a0207f902310138000a0207fa02310138000a0207fb02310138000a0207fc02310138000a0207fd02310138000a0207fe02310138000a0207ff02310138000a02078003310138000a02078103310138000a02078203310138000a02078303310138000a02078403310138000a02078503310138000a02078603310138000a02078703310138000a02078803310138000a02078903310138000a02078a03310138000a02078b03310138000a02078c03310138000a02078d03310138000a02078e03310138000a02078f03310138000a02079003310138000a02079103310138000a02079203310138000a02079303310138000a02079403310138000a02079503310138000a02079603310138000a02079703310138000a02079803310138000a02079903310138000a02079a03310138000a02079b03310138000a02079c03310138000a02079d03310138000a02079e03310138000a02079f03310138000a0207a003310138000a0207a103310138000a0207a203310138000a0207a303310138000a0207a403310138000a0207a503310138000a0207a603310138000a0207a703310138000a0207a803310138000a0207a903310138000a0207aa03310138000a0207ab03310138000a0207ac03310138000a0207ad03310138000a0207ae03310138000a0207af03310138000a0207b003310138000a0207b103310138000a0207b203310138000a0207b303310138000a0207b403310138000a0207b503310138000a0207b603310138000a0207b703310138000a0207b803310138000a0207b903310138000a0207ba03310138000a0207bb03310138000a0207bc03310138000a0207bd03310138000a0207be03310138000a0207bf03310138000a0207c003310138000a0207c103310138000a0207c203310138000a0207c303310138000a0207c403310138000a0207c503310138000a0207c603310138000a0207c703310138000a0207c803310138000a0207c903310138000a0207ca03310138000a0207cb03310138000a0207cc03310138000a0207cd03310138000a0207ce03310138000a0207cf03310138000a0207d003310138000a0207d103310138000a0207d203310138000a0207d303310138000a0207d403310138000a0207d503310138000a0207d603310138000a0207d703310138000a0207d803310138000a0207d903310138000a0207da03310138000a0207db03310138000a0207dc03310138000a0207dd03310138000a0207de03310138000a0207df03310138000a0207e003310138000a0207e103310138000a0207e203310138000a0207e303310138000a0207e403310138000a0207e503310138000a0207e603310138000a0207e703310138000a0207e803310138000a0207e903310138000a0207ea03310138000a0207eb03310138000a0207ec03310138000a0207ed03310138000a0207ee03310138000a0207ef03310138000a0207f003310138000a0207f103310138000a0207f203310138000a0207f303310138000a0207f403310138000a0207f503310138000a0207f603310138000a0207f703310138000a0207f803310138000a0207f903310138000a0207fa03310138000a0207fb03310138000a0207fc03310138000a0207fd03310138000a0207fe03310138000a0207ff03310138000a02078004310138000a02078104310138000a02078204310138000a02078304310138000a02078404310138000a02078504310138000a02078604310138000a02078704310138000a02078804310138000a02078904310138000a02078a04310138000a02078b04310138000a02078c04310138000a02078d04310138000a02078e04310138000a02078f04310138000a02079004310138000a02079104310138000a02079204310138000a02079304310138000a02079404310138000a02079504310138000a02079604310138000a02079704310138000a02079804310138000a02079904310138000a02079a04310138000a02079b04310138000a02079c04310138000a02079d04310138000a02079e04310138000a02079f04310138000a0207a004310138000a0207a104310138000a0207a204310138000a0207a304310138000a0207a404310138000a0207a504310138000a0207a604310138000a0207a704310138000a0207a804310138000a0207a904310138000a0207aa04310138000a0207ab04310138000a0207ac04310138000a0207ad04310138000a0207ae04310138000a0207af04310138000a0207b004310138000a0207b104310138000a0207b204310138000a0207b304310138000a0207b404310138000a0207b504310138000a0207b604310138000a0207b704310138000a0207b804310138000a0207b904310138000a0207ba04310138000a0207bb04310138000a0207bc04310138000a0207bd04310138000a0207be04310138000a0207bf04310138000a0207c004310138000a0207c104310138000a0207c204310138000a0207c304310138000a0207c404310138000a0207c504310138000a0207c604310138000a0207c704310138000a0207c804310138000a0207c904310138000a0207ca04310138000a0207cb04310138000a0207cc04310138000a0207cd04310138000a0207ce04310138000a0207cf04310138000a0207d004310138000a0207d104310138000a0207d204310138000a0207d304310138000a0207d404310138000a0207d504310138000a0207d604310138000a0207d704310138000a0207d804310138000a0207d904310138000a0207da04310138000a0207db04310138000a0207dc04310138000a0207dd04310138000a0207de04310138000a0207df04310138000a0207e004310138000a0207e104310138000a0207e204310138000a0207e304310138000a0207e404310138000a0207e504310138000a0207e604310138000a0207e704310138000a0207e804310138000a0207e904310138000a0207ea04310138000a0207eb04310138000a0207ec04310138000a0207ed04310138000a0207ee04310138000a0207ef04310138000a0207f004310138000a0207f104310138000a0207f204310138000a0207f304310138000a0207f404310138000a0207f504310138000a0207f604310138000a0207f704310138000a0207f804310138000a0207f904310138000a0207fa04310138000a0207fb04310138000a0207fc04310138000a0207fd04310138000a0207fe04310138000a0207ff04310138000a02078005310138000a02078105310138000a02078205310138000a02078305310138000a02078405310138000a02078505310138000a02078605310138000a02078705310138000a02078805310138000a02078905310138000a02078a05310138000a02078b05310138000a02078c05310138000a02078d05310138000a02078e05310138000a02078f05310138000a02079005310138000a02079105310138000a02079205310138000a02079305310138000a02079405310138000a02079505310138000a02079605310138000a02079705310138000a02079805310138000a02079905310138000a02079a05310138000a02079b05310138000a02079c05310138000a02079d05310138000a02079e05310138000a02079f05310138000a0207a005310138000a0207a105310138000a0207a205310138000a0207a305310138000a0207a405310138000a0207a505310138000a0207a605310138000a0207a705310138000a0207a805310138000a0207a905310138000a0207aa05310138000a0207ab05310138000a0207ac05310138000a0207ad05310138000a0207ae05310138000a0207af05310138000a0207b005310138000a0207b105310138000a0207b205310138000a0207b305310138000a0207b405310138000a0207b505310138000a0207b605310138000a0207b705310138000a0207b805310138000a0207b905310138000a0207ba05310138000a0207bb05310138000a0207bc05310138000a0207bd05310138000a0207be05310138000a0207bf05310138000a0207c005310138000a0207c105310138000a0207c205310138000a0207c305310138000a0207c405310138000a0207c505310138000a0207c605310138000a0207c705310138000a0207c805310138000a0207c905310138000a0207ca05310138000a0207cb05310138000a0207cc05310138000a0207cd05310138000a0207ce05310138000a0207cf05310138000a0207d005310138000a0207d105310138000a0207d205310138000a0207d305310138000a0207d405310138000a0207d505310138000a0207d605310138000a0207d705310138000a0207d805310138000a0207d905310138000a0207da05310138000a0207db05310138000a0207dc05310138000a0207dd05310138000a0207de05310138000a0207df05310138000a0207e005310138000a0207e105310138000a0207e205310138000a0207e305310138000a0207e405310138000a0207e505310138000a0207e605310138000a0207e705310138000a0207e805310138000a0207e905310138000a0207ea05310138000a0207eb05310138000a0207ec05310138000a0207ed05310138000a0207ee05310138000a0207ef05310138000a0207f005310138000a0207f105310138000a0207f205310138000a0207f305310138000a0207f405310138000a0207f505310138000a0207f605310138000a0207f705310138000a0207f805310138000a0207f905310138000a0207fa05310138000a0207fb05310138000a0207fc05310138000a0207fd05310138000a0207fe05310138000a0207ff05310138000a02078006310138000a02078106310138000a02078206310138000a02078306310138000a02078406310138000a02078506310138000a02078606310138000a02078706310138000a02078806310138000a02078906310138000a02078a06310138000a02078b06310138000a02078c06310138000a02078d06310138000a02078e06310138000a02078f06310138000a02079006310138000a02079106310138000a02079206310138000a02079306310138000a02079406310138000a02079506310138000a02079606310138000a02079706310138000a02079806310138000a02079906310138000a02079a06310138000a02079b06310138000a02079c06310138000a02079d06310138000a02079e06310138000a02079f06310138000a0207a006310138000a0207a106310138000a0207a206310138000a0207a306310138000a0207a406310138000a0207a506310138000a0207a606310138000a0207a706310138000a0207a806310138000a0207a906310138000a0207aa06310138000a0207ab06310138000a0207ac06310138000a0207ad06310138000a0207ae06310138000a0207af06310138000a0207b006310138000a0207b106310138000a0207b206310138000a0207b306310138000a0207b406310138000a0207b506310138000a0207b606310138000a0207b706310138000a0207b806310138000a0207b906310138000a0207ba06310138000a0207bb06310138000a0207bc06310138000a0207bd06310138000a0207be06310138000a0207bf06310138000a0207c006310138000a0207c106310138000a0207c206310138000a0207c306310138000a0207c406310138000a0207c506310138000a0207c606310138000a0207c706310138000a0207c806310138000a0207c906310138000a0207ca06310138000a0207cb06310138000a0207cc06310138000a0207cd06310138000a0207ce06310138000a0207cf06310138000a0207d006310138000a0207d106310138000a0207d206310138000a0207d306310138000a0207d406310138000a0207d506310138000a0207d606310138000a0207d706310138000a0207d806310138000a0207d906310138000a0207da06310138000a0207db06310138000a0207dc06310138000a0207dd06310138000a0207de06310138000a0207df06310138000a0207e006310138000a0207e106310138000a0207e206310138000a0207e306310138000a0207e406310138000a0207e506310138000a0207e606310138000a0207e706310138000a0207e806310138000a0207e906310138000a0207ea06310138000a0207eb06310138000a0207ec06310138000a0207ed06310138000a0207ee06310138000a0207ef06310138000a0207f006310138000a0207f106310138000a0207f206310138000a0207f306310138000a0207f406310138000a0207f506310138000a0207f606310138000a0207f706310138000a0207f806310138000a0207f906310138000a0207fa06310138000a0207fb06310138000a0207fc06310138000a0207fd06310138000a0207fe06310138000a0207ff06310138000a02078007310138000a02078107310138000a02078207310138000a02078307310138000a02078407310138000a02078507310138000a02078607310138000a02078707310138000a02078807310138000a02078907310138000a02078a07310138000a02078b07310138000a02078c07310138000a02078d07310138000a02078e07310138000a02078f07310138000a02079007310138000a02079107310138000a02079207310138000a02079307310138000a02079407310138000a02079507310138000a02079607310138000a02079707310138000a02079807310138000a02079907310138000a02079a07310138000a02079b07310138000a02079c07310138000a02079d07310138000a02079e07310138000a02079f07310138000a0207a007310138000a0207a107310138000a0207a207310138000a0207a307310138000a0207a407310138000a0207a507310138000a0207a607310138000a0207a707310138000a0207a807310138000a0207a907310138000a0207aa07310138000a0207ab07310138000a0207ac07310138000a0207ad07310138000a0207ae07310138000a0207af07310138000a0207b007310138000a0207b107310138000a0207b207310138000a0207b307310138000a0207b407310138000a0207b507310138000a0207b607310138000a0207b707310138000a0207b807310138000a0207b907310138000a0207ba07310138000a0207bb07310138000a0207bc07310138000a0207bd07310138000a0207be07310138000a0207bf07310138000a0207c007310138000a0207c107310138000a0207c207310138000a0207c307310138000a0207c407310138000a0207c507310138000a0207c607310138000a0207c707310138000a0207c807310138000a0207c907310138000a0207ca07310138000a0207cb07310138000a0207cc07310138000a0207cd07310138000a0207ce07310138000a0207cf07310138000a0207d007310138000a0207d107310138000a0207d207310138000a0207d307310138000a0207d407310138000a0207d507310138000a0207d607310138000a0207d707310138000a0207d807310138000a0207d907310138000a0207da07310138000a0207db07310138000a0207dc07310138000a0207dd07310138000a0207de07310138000a0207df07310138000a0207e007310138000a0207e107310138000a0207e207310138000a0207e307310138000a0207e407310138000a0207e507310138000a0207e607310138000a0207e707310138000a0207e807310138000a0207e907310138000a0207ea07310138000b0207eb0731013800020201000100090807032b000c010b0110000b00380102030100000b1f38020c010d0107ec07310138000d0107ed07310138000d0107ee07310138000d0107ef07310138000d01071a310138000d01071b310138000b000b0112002d00020401040100051c0b0011050c020b02070321030907012707032900030e0701270a011102031307022707032a000c030b030f000b0138030102000000"; - let res = run_binary_test("sample_whitelist", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_coin_store() { - let code = "a11ceb0b050000000e01001e021e0c032acb0604f5065405c907a41207ed19e80908d523800206d5252c108126420ac326090bcc26040cd026fd86010dcdad01020ecfad01040000010101020103020402050306030704080509050a0607060b070c070d000e08010001011104010001000f00010200000010010201000012030101000013040103000000001404010300000000150401030000000016040103000000001704010300000000180401030000000019040103000000001a040103000000001b040103000000001c040103000000001d040103000000001e040103000000001f0401030000000020040103000000002104010300000000220401030000000023040103000000002404010300000000250401030000000026040103000000002704010300000000280401030000000029040103000000002a040103000000002b040103000000002c040103000000002d040103000000002e040103000000002f0401030000000030040103000000003104010300000000320401030000000033050103000000003405010300000000350501030000000036050103000000003705010300000000380501030000000039050103000000003a050103000000003b050103000000003c050103000000003d050103000000003e050103000000003f0501030000000040050103000000004105010300000000420501030000000043050103000000004405010300000000450501030000000046050103000000004705010300000000480501030000000049050103000000004a050103000000004b050103000000004c050103000000004d050103000000004e050103000000004f050103000000005005010300000000510601010000520701010000530801010000540901020000005509010200000056000102000000570a010200000058070102000000590a01020000005a0b01020000005b0001020000005c0c0103000000005d0c01020000005e0c01020000005f0701010000600701010000610201010000620701010000630d01010000640e010100006502030100006601030100006702030100016810020100016911010100096a0114020000046b0100020000016c16030100016d01030100090b1819020000040b1a1b020000016e030101000c6f011d0200000b70031e020000066b0100030000000771221e030000000872010202000008730102020000087424000200000e6a01140200000d0b261e020000037508400002760202000165070301000477014200057801430200000a790100020000060b1a1b03000000011226010100017a48030100580f590f5a135a155b135c0f5d175e135e1559175d0f5f155f136017611361156215600f6320632164216513661366156515671568136815691562136420671369136c0f6e136f135c17702070215817710f720f020303000103010b010109000d030403040404010101010404040d0304030404010101010404040402060c0b0101090002060c0301060c0b030403040404010104040403060c030304060c0303010a060c03040403030404010102050303060c050301090001060b0101090002070b010109000b01010900230b010109000b010109010403040b010109000403040404040b010109010403030304040b010109000b010109000b01010901070b00010900040404040b010109000b010109000b0101090104040b0101090104040209000901020404020901090002070b0101090003010901040b01010900040b0101090104040b010109000b010109010b010109000b01010901040b01010900030b0101090103020b010109000b01010901250303030403040b010109000403040404040b0101090103030304040b010109000b010109010b01010900070b000109000404040b010109000b0101090003040404030b0101090104030403030303010b0101090122040303040b010109000403040404040b0101090103030304040b010109000b010109000b01010901070b000109000404040b0101090003040b010109000404030b0101090104040309000901090203090109000902020b010109000323040404040b010109000403040404040b0101090103030304040b010109000b010109000b01010901070b000109000404050b01010900040b010109000b01010901040b0101090004040b0101090104040805070b01010900070b01010901030301040423040404040b010109000403040404040b0101090103030304040b010109000b010109010b01010900070b00010900040405040b0101090004040b010109000404040b01010901040402050b01010900240403040403040404040403030304040b010109000b010109010b01010900070b000109000b0101090104040504040b010109010b010109000b01010900040b01010900040b010109010b0101090104040b01010900200303030403030404030404040403030304040b010109000b010109010b01010900070b000109000b010109010404030b01010900040303040b010109001d040303030404030404040403030304040b010109000b010109010b01010900070b000109000b01010901040403040b0101090003040b010109001e040404030404030404040403030304040b010109000b010109010b01010900070b000109000b010109010404050b01010900040b010109000b01010901040b010109001e040404030404030404040403030304040b010109000b010109010b01010900070b000109000b0101090104040504040b0101090004040b010109002603030304030403040404040403030304040b010109010b01010900070b00010900040405030b010109000b0101090104040b01010900040b01010900030b01010901040b01010901040304200303030b010109000b010109010403030403040404040403030304040b010109010b01010900070b000109000404030b010109000b01010901040303041d0303030403030304030404040403030304040b01010900070b0001090004040303040303030421030303040404030404030404040403030304040b010109010b01010900070b000109000404050b01010900030b010109000b01010901040303041f0303030404040304030404040403030304040b010109010b01010900070b0001090004040503040403040304230403030403040404040403030304040b010109000b01010901070b0001090004040503040b01010901040b010109000b01010900040b01010900030b01010901040b0101090104041d0b010109000b01010901040303030403040404040403030304040b010109000b01010901070b00010900040403040b010109000b0101090103041e04040403030404030404040403030304040b010109000b01010901070b000109000404050b0101090003040b010109000b0101090103041c040404030304030404040403030304040b010109000b01010901070b00010900040405030404030404240404040403040404040403030304040b010109000b010109000b01010901070b000109000404050b01010901040b01010901040b010109010b010109000b01010900040b01010900040b010109010b0101090104041f04040b010109000b0101090104030403040404040403030304040b010109000b010109000b01010901070b000109000404050b01010901040b010109010b010109000b0101090104200404030303040304030404040403030304040b010109000b01010900070b000109000404050b01010901030b010109010b01010900040303041d040404030304030404040403030304040b010109000b01010900070b000109000404050b0101090103040b010109010b0101090003041e040404040404030404040403030304040b010109000b010109010b01010900070b000109000404050b01010901040b01010900040b010109010404240404040403040404040403030304040b010109000b010109000b01010901070b00010900040405040b010109000b0101090104040b01010900040b01010900040b01010901040b0101090104041f04040b010109000b0101090104030403040404040403030304040b010109000b010109000b01010901070b0001090004040504040b010109000b0101090104041f0404030303040304030404040403030304040b010109000b01010900070b00010900040405040304040303041c040404030304030404040403030304040b010109000b01010900070b000109000404050403040403041f04040404040404030404040403030304040b010109000b010109000b01010901070b000109000404050b01010900040b010109000b01010901040404010b0001090001050d05030403070b000109000b01010901040b01010900040303030b01010900040302010101011a0504040404070b00010900070b000109010404030303030b01010901040404040b010109000b010109000b010109000b010109000b010109010b010109010b010109010b010109010f03030305040404030b010109010b01010901040b0101090003030b010109000e04040305040404030b010109000b01010901040b010109010b010109010b010109000f030303030305040404030b010109010403030301070b01010900030b0101090003070b000109000a636f696e5f73746f726504636f696e056572726f72067369676e65720f416e696d6553776170506f6f6c563116416e696d6553776170506f6f6c56314c6962726172790e6c69717569646974795f706f6f6c06726f7574657203616d6d09546f6b656e537761700f546f6b656e53776170436f6e66696704737761700a616d6d5f726f7574657208616d6d5f737761700b4d79436f696e53746f726512616e696d65737761705f73746172737761700762616c616e636504436f696e076465706f736974066c3073307431066c3073317430066c3173307431066c3173307432066c3173307433066c3173307434066c3173307435066c3173317430066c3173317432066c3173317433066c3173317434066c3173317435066c3173327430066c3173327431066c3173327433066c3173327434066c3173327435066c3173337430066c3173337431066c3173337432066c3173337434066c3173337435066c3173347430066c3173347431066c3173347432066c3173347433066c3173347435066c3173357430066c3173357431066c3173357432066c3173357433066c3173357434066c3273307431066c3273307432066c3273307433066c3273307434066c3273307435066c3273317430066c3273317432066c3273317433066c3273317434066c3273317435066c3273327430066c3273327431066c3273327433066c3273327434066c3273327435066c3273337430066c3273337431066c3273337432066c3273337434066c3273337435066c3273347430066c3273347431066c3273347432066c3273347433066c3273347435066c3273357430066c3273357431066c3273357432066c3273357433066c3273357434036e65770d6e65775f66726f6d5f6d61696e0d6e65775f66726f6d5f7a65726f047330743104733174301073696d706c655f616e696d65737761701373696d706c655f616e696d65737761705f76320f73696d706c655f73746172737761701273696d706c655f73746172737761705f76321273696d706c655f73746172737761705f76331273746172737761705f616e696d657377617012737761705f61625f6c69717569647377617008737761705f61757810737761705f70616e63616b6573776170127472616e736665725f66726f6d5f6d61696e197472616e736665725f66726f6d5f6d61696e5f736372697074107472616e736665725f746f5f6d61696e177472616e736665725f746f5f6d61696e5f736372697074117472616e736665725f746f5f6f74686572187472616e736665725f746f5f6f746865725f7363726970740877697468647261770c77697468647261775f616c6c0c77697468647261775f616e790576616c7565056d657267650c6765745f7265736572766573116765745f72657365727665735f73697a650765787472616374047a65726f0c64657374726f795f7a65726f0e746f6b656e5f726573657276657321737761705f65786163745f785f746f5f795f6469726563745f65787465726e616c18737761705f65786163745f636f696e5f666f725f636f696e04785f617504795f61751c737761705f65786163745f636f696e5f666f725f636f696e5f6d75740a616464726573735f6f66117065726d697373696f6e5f64656e6965640e6765745f61646d696e5f6461746107636f6d70617265116765745f706f756e646167655f726174650b657874726163745f616c6cd1b58e44ea11ffd326a280c453b080fd8af294298815a00e65b6e2bffc48d6ac000000000000000000000000000000000000000000000000000000000000000116fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12bd35135844473187163ca197ca93b2ab014370587bb0ed3befff9e902d6bb541c755e4c8d7a6ab6d56f9289d97c43c1c94bde75ec09147c90d35cd1be61c8fb9c7efb4076dbe143cbcd98cfaaa929ecfc8f299203dfff63b95ccb6bfe19850faec42a352cc65eca17a9fa85d0fc602295897ed6b8b8af6a6c79ef490eb8f9eba0308915f0100000000000520d1b58e44ea11ffd326a280c453b080fd8af294298815a00e65b6e2bffc48d6ac126170746f733a3a6d657461646174615f76302e01915f010000000000234552524f525f5448455f4f55545055545f4c4553535f5448414e5f4d494e5f5245435600000201010b01010900000f001700010400010206ffffffffffffffff270101000100010507013c0037003800020201000100010607013c0036000b0038010203010400010206ffffffffffffffff2704010400010206ffffffffffffffff2705010400010206ffffffffffffffff2706010400010206ffffffffffffffff2707010400010206ffffffffffffffff2708010400010206ffffffffffffffff2709010400010206ffffffffffffffff270a010400010206ffffffffffffffff270b010400010206ffffffffffffffff270c010400010206ffffffffffffffff270d010400010206ffffffffffffffff270e010400010206ffffffffffffffff270f010400010206ffffffffffffffff2710010400010206ffffffffffffffff2711010400010206ffffffffffffffff2712010400010206ffffffffffffffff2713010400010206ffffffffffffffff2714010400010206ffffffffffffffff2715010400010206ffffffffffffffff2716010400010206ffffffffffffffff2717010400010206ffffffffffffffff2718010400010206ffffffffffffffff2719010400010206ffffffffffffffff271a010400010206ffffffffffffffff271b010400010206ffffffffffffffff271c010400010206ffffffffffffffff271d010400010206ffffffffffffffff271e010400010206ffffffffffffffff271f010400010206ffffffffffffffff2720010400010206ffffffffffffffff2721010400010206ffffffffffffffff2722010400010206ffffffffffffffff27230104010012f1010b05040638020c2c0c26050d38030c2b0c2e0b2b0c260b2e0c2c0b260b2c0c180c1738040c10350b10350c1f0c1e0a0432000000000000000000000000000000002104660a180a0c180a0a1a0a1f160c110a170a1f180a111a0c0f0a1e0a18180a0c180a0a1a0b111a0c110a0f0a11180a0b180a091a0c2f0a0f0a11180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2f23045e054d0a270c2f0a0f0a11180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c2705480b2f0b0f170a0b180a091a0c040a040b0b180c150b170b09180a15160c240b150b18180b241a320b000000000000000000000000000000170c130a130b0c180c160b1f0b0a180a16160c250b160b1e180b251a320b000000000000000000000000000000170c1a0a1a0a04170b0317010b04340c1407013c000c230a23370038000c1b0a2336000b1438050c200b0804ab010b200b133806320000000000000000000000000000000038070c190c120c2d0c2805b401380632000000000000000000000000000000000b200b1338080c120c190c280c2d0d280b1238010d2d0b1938090b2d0c220a2336000b283801380a0c290b0704cd010b220600000000000000000b290b1a34380b0c0d0c0e05d5010b290b1a340b22060000000000000000380c0c0e0c0d0b0e0b0d0c210c2a0a2336000b2138010b1b0b02160c1c0b23370038000c1d0a1d0a1c2404ea0105ee010b1c0b1d17270b2a380d0224010401001cef010b05040638020c2c0c26050d38030c2b0c2f0b2b0c260b2f0c2c0b260b2c0c190c180b060418380e0c0f0c0e0c0d0522380f0c300c290c2d0b290b2d0b300c0f0c0e0c0d0b0d0b0e0b0f010c11350b11350c1f0c1e0a04320000000000000000000000000000000021047a0a190a0c180a0a1a0a1f160c120a180a1f180a121a0c100a1e0a19180a0c180a0a1a0b121a0c120a100a12180a0b180a091a0c310a100a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c2a0a2a0a3123047205610a2a0c310a100a12180a0b180a091a0a2a1a0b2a1632020000000000000000000000000000001a0c2a055c0b310b10170a0b180a091a0c040a040b0b180c160b180b09180a16160c240b160b19180b241a320b000000000000000000000000000000170c140a140b0c180c170b1f0b0a180a17160c250b170b1e180b251a320b000000000000000000000000000000170a04170b0317010b04340c1507013c000c230a23370038000c1b0a2336000b1538050c200b0804bd010b200b143806320000000000000000000000000000000038070c1a0c130c2e0c2705c601380632000000000000000000000000000000000b200b1438080c130c1a0c270c2e0d270b1338010d2e0b1a38090b2e0c210a2336000b273801380a0c280b2138100c220a2336000b2238010b1b0b02160c1c0b23370038000c1d0a1d0a1c2404e80105ec010b1c0b1d17270b2838110225010401001fe8010b05040638020c2a0c24050d38030c290c2d0b290c240b2d0c2a0b240b2a0c170c160b06041738120c0f0c0e051e38130c260c2b0b260c0e0b2b0c0f0b0e350b0f350c1d0c1c0a0432000000000000000000000000000000002104720a170a0c180a0a1a0a1d160c100a160a1d180a101a0c0d0a1c0a17180a0c180a0a1a0b101a0c100a0d0a10180a0b180a091a0c2e0a0d0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2e23046a05590a270c2e0a0d0a10180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c2705540b2e0b0d170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1d0b0a180a15160c230b150b1c180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307013c000c210a21370038000c190a2136000b1338050c1e0b0804b5010b1e0b123806320000000000000000000000000000000038070c180c110c2c0c2805be01380632000000000000000000000000000000000b1e0b1238080c110c180c280c2c0d280b1138010d2c0b1838090b2c0c200a2136000b283801380a0c250b2006000000000000000038140c1f0a2136000b1f38010b190b02160c1a0b21370038000c1b0a1b0a1a2404e10105e5010b1a0b1b17270b25381102260104010023f8010b05040638020c2c0c26050d38030c2b0c2e0b2b0c260b2e0c2c0b260b2c0c170c160b06041a3815353816350c0e0c0d05203817353818350c0e0c0d0b0d0b0e0c1d0c1c0a0432000000000000000000000000000000002104720a170a0c180a0a1a0a1d160c100a160a1d180a101a0c0f0a1c0a17180a0c180a0a1a0b101a0c100a0f0a10180a0b180a091a0c2f0a0f0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c290a290a2f23046a05590a290c2f0a0f0a10180a0b180a091a0a291a0b291632020000000000000000000000000000001a0c2905540b2f0b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1d0b0a180a15160c230b150b1c180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307010c240a243c000c210a21370038000c190a2136000b1338050c1e0b0804b7010b1e0a123806320000000000000000000000000000000038070c180c110c2d0c2a05c001380632000000000000000000000000000000000b1e0a1238080c110c180c2a0c2d0d2a0b1138010d2d0b1838090b2d0c200a2136000b2a3801380a0c270b240d200d270b12340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381901010b270c250b200c280b250c1f0a2136000b1f38010b190b02160c1a0b21370038000c1b0a1b0a1a2404f10105f5010b1a0b1b17270b28380d02270104010025e8010b05040638020c2b0c25050d38030c2a0c2e0b2a0c250b2e0c2b0b250b2b0c170c160b060417381a0c0e0c0d051e381b0c270c2c0b270b2c0c0e0c0d0b0d0b0e0c1d0c1c0a0432000000000000000000000000000000002104700a170a0c180a0a1a0a1d160c100a160a1d180a101a0c0f0a1c0a17180a0c180a0a1a0b101a0c100a0f0a10180a0b180a091a0c2f0a0f0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c280a280a2f23046805570a280c2f0a0f0a10180a0b180a091a0a281a0b281632020000000000000000000000000000001a0c2805520b2f0b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1d0b0a180a15160c230b150b1c180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307010c240a243c000c210a21370038000c190a2136000b1338050c1e0b0804b5010b1e0b123806320000000000000000000000000000000038070c180c110c2d0c2905be01380632000000000000000000000000000000000b1e0b1238080c110c180c290c2d0d290b1138010d2d0b1838090b2d0c1f0a2136000b293801380a0c260b240b1f381c0c200a2136000b2038010b190b02160c1a0b21370038000c1b0a1b0a1a2404e10105e5010b1a0b1b17270b26381102280104010027f20138040c0e350b0e350c150c140b06040d38020c2b0c24051438030c290c2e0b290c240b2e0c2b0b240b2b0c1b0c1a0a0432000000000000000000000000000000002104660a150a0c180a0a1a0a1b160c0f0a140a1b180a0f1a0c0d0a1a0a15180a0c180a0a1a0b0f1a0c0f0a0d0a0f180a0b180a091a0c2f0a0d0a0f180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2f23045e054d0a250c2f0a0d0a0f180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c2505480b2f0b0d170a0b180a091a0c040a040b0b180c120b140b09180a12160c210b120b15180b211a320b000000000000000000000000000000170c100a100b0c180c130b1b0b0a180a13160c220b130b1a180b221a320b000000000000000000000000000000170c160a160a04170b0317010b04340c1107010c230a233c000c1f0a1f370038000c170a1f36000b1138050c1c0b0804ac010b1c06000000000000000038060b1034380c0c200c3005b40138060b10340b1c060000000000000000380b0c300c200b300b200c1d0c270b0704c4010b1d0b160b27320000000000000000000000000000000038080c280c2d0c2a0c2c05cd010b2732000000000000000000000000000000000b1d0b1638070c2d0c280c2c0c2a0d2a0b2838010d2c0b2d38090b2a0c1e0b2c0c260a1f36000b1e38010b170b02160c180b1f370038000c190a190a182404e80105ec010b180b1917270b233c0136010b26380902290104010028d90138040c11350b11350c190c180b06040e380e0c0f0c0e0c0d0518380f0c2a0c260c290b260b290b2a0c0f0c0e0c0d0b0d0b0e0b0f010c12350b12350c1e0c1d0a0432000000000000000000000000000000002104700a190a0c180a0a1a0a1e160c130a180a1e180a131a0c100a1d0a19180a0c180a0a1a0b131a0c130a100a13180a0b180a091a0c2b0a100a13180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c280a280a2b23046805570a280c2b0a100a13180a0b180a091a0a281a0b281632020000000000000000000000000000001a0c2805520b2b0b10170a0b180a091a0c040a040b0b180c160b180b09180a16160c240b160b19180b241a320b000000000000000000000000000000170c140a140b0c180c170b1e0b0a180a17160c250b170b1d180b251a320b000000000000000000000000000000170a04170b0317010b04340c1507013c000c220a22370038000c1a0a2236000b1538050c1f0b0804b2010b1f06000000000000000038060b1434380c0c230c2c05ba0138060b14340b1f060000000000000000380b0c2c0c230b2c0b230c200c270b2038100c210a2236000b2138010b1a0b02160c1b0b22370038000c1c0a1c0a1b2404d20105d6010b1b0b1c17270b273811022a0104010029d20138040c0f350b0f350c170c160b06040d38120c100c0e051438130c240c270b240c0e0b270c100b0e350b10350c1c0c1b0a0432000000000000000000000000000000002104680a170a0c180a0a1a0a1c160c110a160a1c180a111a0c0d0a1b0a17180a0c180a0a1a0b111a0c110a0d0a11180a0b180a091a0c280a0d0a11180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a28230460054f0a250c280a0d0a11180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c25054a0b280b0d170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1c0b0a180a15160c230b150b1b180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307013c000c200a20370038000c180a2036000b1338050c1d0b0804aa010b1d06000000000000000038060b1234380c0c210c2905b20138060b12340b1d060000000000000000380b0c290c210b290b210c1e0c260b1e06000000000000000038140c1f0a2036000b1f38010b180b02160c190b20370038000c1a0a1a0a192404cb0105cf010b190b1a17270b263811022b010401002ae20138040c10350b10350c170c160b0604103815353816350c0e0c0d05163817353818350c0e0c0d0b0d0b0e0c1c0c1b0a0432000000000000000000000000000000002104680a170a0c180a0a1a0a1c160c110a160a1c180a111a0c0f0a1b0a17180a0c180a0a1a0b111a0c110a0f0a11180a0b180a091a0c290a0f0a11180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c260a260a29230460054f0a260c290a0f0a11180a0b180a091a0a261a0b261632020000000000000000000000000000001a0c26054a0b290b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1c0b0a180a15160c230b150b1b180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307010c240a243c000c200a20370038000c180a2036000b1338050c1d0b0804ac010b1d06000000000000000038060a1234380c0c210c2a05b40138060a12340b1d060000000000000000380b0c2a0c210b2a0b210c1e0c270b240d1e0d270b12340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381901010b270c250b1e0c280b250c1f0a2036000b1f38010b180b02160c190b20370038000c1a0a1a0a192404db0105df010b190b1a17270b28380d022c010401002bd20138040c10350b10350c170c160b06040d381a0c0e0c0d0514381b0c250c280b250b280c0e0c0d0b0d0b0e0c1c0c1b0a0432000000000000000000000000000000002104660a170a0c180a0a1a0a1c160c110a160a1c180a111a0c0f0a1b0a17180a0c180a0a1a0b111a0c110a0f0a11180a0b180a091a0c290a0f0a11180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c260a260a2923045e054d0a260c290a0f0a11180a0b180a091a0a261a0b261632020000000000000000000000000000001a0c2605480b290b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c220b140b17180b221a320b000000000000000000000000000000170c120a120b0c180c150b1c0b0a180a15160c230b150b1b180b231a320b000000000000000000000000000000170a04170b0317010b04340c1307010c240a243c000c200a20370038000c180a2036000b1338050c1d0b0804aa010b1d06000000000000000038060b1234380c0c210c2a05b20138060b12340b1d060000000000000000380b0c2a0c210b2a0b210c1e0c270b240b1e381c0c1f0a2036000b1f38010b180b02160c190b20370038000c1a0a1a0a192404cb0105cf010b190b1a17270b273811022d010401002cec010b050407380e0c0f0c0e0c0d0511380f0c310c240c2c0b240b2c0b310c0f0c0e0c0d0b0d0b0e0b0f010c11350b11350c170c160b06042138020c2e0c27052838030c2a0c300b2a0c270b300c2e0b270b2e0c1d0c1c0a04320000000000000000000000000000000021047a0a170a0c180a0a1a0a1d160c120a160a1d180a121a0c100a1c0a17180a0c180a0a1a0b121a0c120a100a12180a0b180a091a0c320a100a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c280a280a3223047205610a280c320a100a12180a0b180a091a0a281a0b281632020000000000000000000000000000001a0c28055c0b320b10170a0b180a091a0c040a040b0b180c140b160b09180a14160c210b140b17180b211a0b0c180c150b1d0b0a180a15160c220b150b1c180b221a0c180a180a04170b0317010b04340c1307010c230a233c000c200a20370038000c190a2036000b133805381d0c1e380a0c250b0704be010b1e0b180b25320000000000000000000000000000000038080c290c2f0c2b0c2d05c7010b2532000000000000000000000000000000000b1e0b1838070c2f0c290c2d0c2b0d2b0b2938010d2d0b2f38090b2b0c1f0b2d0c260a2036000b1f38010b190b02160c1a0b20370038000c1b0a1b0a1a2404e20105e6010b1a0b1b17270b233c0136010b263809022e010401002dd5010b050407380e0c0f0c0e0c0d0511380f0c2b0c260c2a0b260b2a0b2b0c0f0c0e0c0d0b0d0b0e0b0f010c13350b13350c1a0c1938040c14350b14350c200c1f0a0432000000000000000000000000000000002104700a1a0a0c180a0a1a0a20160c150a190a20180a151a0c120a1f0a1a180a0c180a0a1a0b151a0c150a120a15180a0b180a091a0c2c0a120a15180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c290a290a2c23046805570a290c2c0a120a15180a0b180a091a0a291a0b291632020000000000000000000000000000001a0c2905520b2c0b12170a0b180a091a0c040a040b0b180c170b190b09180a17160c240b170b1a180b241a0b0c180c180b200b0a180a18160c250b180b1f180b251a0c1b0a1b0a04170b0317010b04340c1607013c000c230a23370038000c1c0a2336000b163805381d0c21380a0c270b0704b1010b210600000000000000000b270b1b34380b0c100c1105b9010b270b1b340b21060000000000000000380c0c110c100b110b100c220c280a2336000b2238010b1c0b02160c1d0b23370038000c1e0a1e0a1d2404ce0105d2010b1d0b1e17270b28380d022f010401002ec6010b050407380e0c0f0c0e0c0d0511380f0c280c230c260b230b260b280c0f0c0e0c0d0b0d0b0e0b0f010c12350b12350c190c180b06042138120c130c11052838130c240c270b240c110b270c130b11350b13350c1e0c1d0a04320000000000000000000000000000000021047c0a190a0c180a0a1a0a1e160c140a180a1e180a141a0c100a1d0a19180a0c180a0a1a0b141a0c140a100a14180a0b180a091a0c290a100a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2923047405630a250c290a100a14180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c25055e0b290b10170a0b180a091a0c040a040b0b180c160b180b09180a16160c210b160b19180b211a0b0c180c170b1e0b0a180a17160c220b170b1d180b221a0a04170b0317010b04340c1507013c000c200a20370038000c1a0a2036000b153805381d06000000000000000038140c1f0a2036000b1f38010b1a0b02160c1b0b20370038000c1c0a1c0a1b2404c10105c5010b1b0b1c17270230010401002fde010b050407380e0c0f0c0e0c0d0511380f0c2c0c270c2b0b270b2b0b2c0c0f0c0e0c0d0b0d0b0e0b0f010c13350b13350c1a0c190b0604243815353816350c110c10052a3817353818350c110c100b100b110c1f0c1e0a04320000000000000000000000000000000021047c0a1a0a0c180a0a1a0a1f160c140a190a1f180a141a0c120a1e0a1a180a0c180a0a1a0b141a0c140a120a14180a0b180a091a0c2d0a120a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c2a0a2a0a2d23047405630a2a0c2d0a120a14180a0b180a091a0a2a1a0b2a1632020000000000000000000000000000001a0c2a055e0b2d0b12170a0b180a091a0c040a040b0b180c170b190b09180a17160c230b170b1a180b231a0c150a150b0c180c180b1f0b0a180a18160c240b180b1e180b241a0a04170b0317010b04340c1607010c250a253c000c220a22370038000c1b0a2236000b163805381d0c20380a0c280b250d200d280b15340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381901010b280c260b200c290b260c210a2236000b2138010b1b0b02160c1c0b22370038000c1d0a1d0a1c2404d70105db010b1c0b1d17270b29380d02310104010030c8010b050407380e0c0f0c0e0c0d0511380f0c2a0c250c280b250b280b2a0c0f0c0e0c0d0b0d0b0e0b0f010c13350b13350c190c180b060421381a0c110c100528381b0c260c290b260b290c110c100b100b110c1e0c1d0a04320000000000000000000000000000000021047a0a190a0c180a0a1a0a1e160c140a180a1e180a141a0c120a1d0a19180a0c180a0a1a0b141a0c140a120a14180a0b180a091a0c2b0a120a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2b23047205610a270c2b0a120a14180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c27055c0b2b0b12170a0b180a091a0c040a040b0b180c160b180b09180a16160c220b160b19180b221a0b0c180c170b1e0b0a180a17160c230b170b1d180b231a0a04170b0317010b04340c1507010c240a243c000c210a21370038000c1a0a2136000b153805381d0c1f0b240b1f381c0c200a2136000b2038010b1a0b02160c1b0b21370038000c1c0a1c0a1b2404c30105c7010b1b0b1c172702320104010031e5010b05040638120c0f0c0e050d38130c220c2a0b220c0e0b2a0c0f0b0e350b0f350c150c140b06041938020c2c0c23052038030c280c2e0b280c230b2e0c2c0b230b2c0c1b0c1a0a0432000000000000000000000000000000002104720a150a0c180a0a1a0a1b160c100a140a1b180a101a0c0d0a1a0a15180a0c180a0a1a0b101a0c100a0d0a10180a0b180a091a0c2f0a0d0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2f23046a05590a250c2f0a0d0a10180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c2505540b2f0b0d170a0b180a091a0c040a040b0b180c120b140b09180a12160c1f0b120b15180b1f1a0b0c180c130b1b0b0a180a13160c200b130b1a180b201a0c160a160a04170b0317010b04340c1107010c210a213c000c1e0a1e370038000c170a1e36000b113805060000000000000000381e0c1d380a0c260b0704b7010b1d0b160b26320000000000000000000000000000000038080c270c2d0c290c2b05c0010b2632000000000000000000000000000000000b1d0b1638070c2d0c270c2b0c290d290b2738010d2b0b2d38090b290c1c0b2b0c240a1e36000b1c38010b170b02160c180b1e370038000c190a190a182404db0105df010b180b1917270b213c0136010b24380902330104010032ce010b05040638120c110c10050d38130c240c280b240c100b280c110b10350b11350c180c1738040c12350b12350c1e0c1d0a0432000000000000000000000000000000002104680a180a0c180a0a1a0a1e160c130a170a1e180a131a0c0f0a1d0a18180a0c180a0a1a0b131a0c130a0f0a13180a0b180a091a0c290a0f0a13180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a29230460054f0a250c290a0f0a13180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c25054a0b290b0f170a0b180a091a0c040a040b0b180c150b170b09180a15160c220b150b18180b221a0b0c180c160b1e0b0a180a16160c230b160b1d180b231a0c190a190a04170b0317010b04340c1407013c000c210a21370038000c1a0a2136000b143805060000000000000000381e0c20380a0c260b0704aa010b200600000000000000000b260b1934380b0c0d0c0e05b2010b260b19340b20060000000000000000380c0c0e0c0d0b0e0b0d0c1f0c270a2136000b1f38010b1a0b02160c1b0b21370038000c1c0a1c0a1b2404c70105cb010b1b0b1c17270b27380d0234010401002ec6010b05040638120c120c11050d38130c230c260b230c110b260c120b11350b12350c190c180b06041a380e0c0f0c0e0c0d0524380f0c280c240c270b240b270b280c0f0c0e0c0d0b0d0b0e0b0f010c13350b13350c1e0c1d0a04320000000000000000000000000000000021047c0a190a0c180a0a1a0a1e160c140a180a1e180a141a0c100a1d0a19180a0c180a0a1a0b141a0c140a100a14180a0b180a091a0c290a100a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2923047405630a250c290a100a14180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c25055e0b290b10170a0b180a091a0c040a040b0b180c160b180b09180a16160c210b160b19180b211a0b0c180c170b1e0b0a180a17160c220b170b1d180b221a0a04170b0317010b04340c1507013c000c200a20370038000c1a0a2036000b153805060000000000000000381e38100c1f0a2036000b1f38010b1a0b02160c1b0b20370038000c1c0a1c0a1b2404c10105c5010b1b0b1c172702350104010033d7010b05040638120c110c10050d38130c250c290b250c100b290c110b10350b11350c180c170b06041c3815353816350c0e0c0d05223817353818350c0e0c0d0b0d0b0e0c1d0c1c0a0432000000000000000000000000000000002104740a180a0c180a0a1a0a1d160c120a170a1d180a121a0c0f0a1c0a18180a0c180a0a1a0b121a0c120a0f0a12180a0b180a091a0c2a0a0f0a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c260a260a2a23046c055b0a260c2a0a0f0a12180a0b180a091a0a261a0b261632020000000000000000000000000000001a0c2605560b2a0b0f170a0b180a091a0c040a040b0b180c150b170b09180a15160c210b150b18180b211a0c130a130b0c180c160b1d0b0a180a16160c220b160b1c180b221a0a04170b0317010b04340c1407010c230a233c000c200a20370038000c190a2036000b143805060000000000000000381e0c1f380a0c270b230d1f0d270b13340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381901010b270c240b1f0c280b240c1e0a2036000b1e38010b190b02160c1a0b20370038000c1b0a1b0a1a2404d00105d4010b1a0b1b17270b28380d02360104010034c1010b05040638120c110c10050d38130c230c260b230c100b260c110b10350b11350c170c160b060419381a0c0e0c0d0520381b0c240c270b240b270c0e0c0d0b0d0b0e0c1c0c1b0a0432000000000000000000000000000000002104720a170a0c180a0a1a0a1c160c120a160a1c180a121a0c0f0a1b0a17180a0c180a0a1a0b121a0c120a0f0a12180a0b180a091a0c280a0f0a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2823046a05590a250c280a0f0a12180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c2505540b280b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c200b140b17180b201a0b0c180c150b1c0b0a180a15160c210b150b1b180b211a0a04170b0317010b04340c1307010c220a223c000c1f0a1f370038000c180a1f36000b133805060000000000000000381e0c1e0b220b1e381c0c1d0a1f36000b1d38010b180b02160c190b1f370038000c1a0a1a0a192404bc0105c0010b190b1a172702370104010035f9010b0504093815353816350c0e0c0d050f3817353818350c0e0c0d0b0d0b0e0c150c140b06041938020c2c0c24052038030c2a0c2f0b2a0c240b2f0c2c0b240b2c0c1b0c1a0a0432000000000000000000000000000000002104720a150a0c180a0a1a0a1b160c100a140a1b180a101a0c0f0a1a0a15180a0c180a0a1a0b101a0c100a0f0a10180a0b180a091a0c300a0f0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c260a260a3023046a05590a260c300a0f0a10180a0b180a091a0a261a0b261632020000000000000000000000000000001a0c2605540b300b0f170a0b180a091a0c040a040b0b180c120b140b09180a12160c200b120b15180b201a320b000000000000000000000000000000170b0c180c130b1b0b0a180a13160c210b130b1a180b211a320b000000000000000000000000000000170c160a160a04170b0317010b04340c1107010c220a223c000c1f0a1f370038000c170a1f36000a1138050c1c38060c270a220d1c0d270b11340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b270c230b1c0c280b230c1e0b0704cb010b1e0b160b28320000000000000000000000000000000038080c290c2e0c2b0c2d05d4010b2832000000000000000000000000000000000b1e0b1638070c2e0c290c2d0c2b0d2b0b2938010d2d0b2e38090b2b0c1d0b2d0c250a1f36000b1d38010b170b02160c180b1f370038000c190a190a182404ef0105f3010b180b1917270b223c0136010b25380902380104010036e4010b0504093815353816350c0e0c0d050f3817353818350c0e0c0d0b0d0b0e0c180c1738040c12350b12350c1e0c1d0a0432000000000000000000000000000000002104680a180a0c180a0a1a0a1e160c130a170a1e180a131a0c110a1d0a18180a0c180a0a1a0b131a0c130a110a13180a0b180a091a0c2b0a110a13180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2b230460054f0a270c2b0a110a13180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c27054a0b2b0b11170a0b180a091a0c040a040b0b180c150b170b09180a15160c230b150b18180b231a320b000000000000000000000000000000170b0c180c160b1e0b0a180a16160c240b160b1d180b241a320b000000000000000000000000000000170c190a190a04170b0317010b04340c1407010c250a253c000c220a22370038000c1a0a2236000a1438050c1f38060c280b250d1f0d280b14340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b280c260b1f0c290b260c210b0704c0010b210600000000000000000b290b1934380b0c0f0c1005c8010b290b19340b21060000000000000000380c0c100c0f0b100b0f0c200c2a0a2236000b2038010b1a0b02160c1b0b22370038000c1c0a1c0a1b2404dd0105e1010b1b0b1c17270b2a380d02390104010037e0010b0504093815353816350c0e0c0d050f3817353818350c0e0c0d0b0d0b0e0c190c180b06041a380e0c110c100c0f0524380f0c2b0c260c2a0b260b2a0b2b0c110c100c0f0b0f0b100b11010c13350b13350c1e0c1d0a04320000000000000000000000000000000021047c0a190a0c180a0a1a0a1e160c140a180a1e180a141a0c120a1d0a19180a0c180a0a1a0b141a0c140a120a14180a0b180a091a0c2c0a120a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c290a290a2c23047405630a290c2c0a120a14180a0b180a091a0a291a0b291632020000000000000000000000000000001a0c29055e0b2c0b12170a0b180a091a0c040a040b0b180c160b180b09180a16160c220b160b19180b221a320b000000000000000000000000000000170b0c180c170b1e0b0a180a17160c230b170b1d180b231a320b000000000000000000000000000000170a04170b0317010b04340c1507010c240a243c000c210a21370038000c1a0a2136000a1538050c1f38060c270b240d1f0d270b15340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b270c250b1f0c280b2538100c200a2136000b2038010b1a0b02160c1b0b21370038000c1c0a1c0a1b2404d90105dd010b1b0b1c17270b283811023a0104010038d9010b0504093815353816350c0e0c0d050f3817353818350c0e0c0d0b0d0b0e0c170c160b06041938120c110c10052038130c240c280b240c100b280c110b10350b11350c1c0c1b0a0432000000000000000000000000000000002104740a170a0c180a0a1a0a1c160c120a160a1c180a121a0c0f0a1b0a17180a0c180a0a1a0b121a0c120a0f0a12180a0b180a091a0c290a0f0a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2923046c055b0a250c290a0f0a12180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c2505560b290b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c200b140b17180b201a320b000000000000000000000000000000170b0c180c150b1c0b0a180a15160c210b150b1b180b211a320b000000000000000000000000000000170a04170b0317010b04340c1307010c220a223c000c1f0a1f370038000c180a1f36000a1338050c1d38060c260b220d1d0d260b13340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b260c230b1d0c270b2306000000000000000038140c1e0a1f36000b1e38010b180b02160c190b1f370038000c1a0a1a0a192404d20105d6010b190b1a17270b273811023b0104010039d9010b0504093815353816350c0e0c0d050f3817353818350c0e0c0d0b0d0b0e0c170c160b060419381a0c100c0f0520381b0c250c290b250b290c100c0f0b0f0b100c1c0c1b0a0432000000000000000000000000000000002104720a170a0c180a0a1a0a1c160c120a160a1c180a121a0c110a1b0a17180a0c180a0a1a0b121a0c120a110a12180a0b180a091a0c2a0a110a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2a23046a05590a270c2a0a110a12180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c2705540b2a0b11170a0b180a091a0c040a040b0b180c140b160b09180a14160c210b140b17180b211a320b000000000000000000000000000000170b0c180c150b1c0b0a180a15160c220b150b1b180b221a320b000000000000000000000000000000170a04170b0317010b04340c1307010c230a233c000c200a20370038000c180a2036000a1338050c1d38060c280a230d1d0d280b13340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b280c240b1d0c260b240c1e0b230b1e381c0c1f0a2036000b1f38010b180b02160c190b20370038000c1a0a1a0a192404d20105d6010b190b1a17270b263811023c010401003ae5010b050406381a0c0e0c0d050d381b0c230c2b0b230b2b0c0e0c0d0b0d0b0e0c150c140b06041738020c2d0c26051e38030c290c2f0b290c260b2f0c2d0b260b2d0c1b0c1a0a0432000000000000000000000000000000002104700a150a0c180a0a1a0a1b160c100a140a1b180a101a0c0f0a1a0a15180a0c180a0a1a0b101a0c100a0f0a10180a0b180a091a0c300a0f0a10180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a3023046805570a270c300a0f0a10180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c2705520b300b0f170a0b180a091a0c040a040b0b180c120b140b09180a12160c200b120b15180b201a0b0c180c130b1b0b0a180a13160c210b130b1a180b211a0c160a160a04170b0317010b04340c1107010c220a223c000c1f0a1f370038000c170a1f36000b1138050c1c0a220b1c38200c1e380a0c240b0704b7010b1e0b160b24320000000000000000000000000000000038080c280c2e0c2a0c2c05c0010b2432000000000000000000000000000000000b1e0b1638070c2e0c280c2c0c2a0d2a0b2838010d2c0b2e38090b2a0c1d0b2c0c250a1f36000b1d38010b170b02160c180b1f370038000c190a190a182404db0105df010b180b1917270b223c0136010b253809023d010401003bd0010b050406381a0c0e0c0d050d381b0c260c2a0b260b2a0c0e0c0d0b0d0b0e0c180c1738040c12350b12350c1e0c1d0a0432000000000000000000000000000000002104660a180a0c180a0a1a0a1e160c130a170a1e180a131a0c110a1d0a18180a0c180a0a1a0b131a0c130a110a13180a0b180a091a0c2b0a110a13180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2b23045e054d0a270c2b0a110a13180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c2705480b2b0b11170a0b180a091a0c040a040b0b180c150b170b09180a15160c230b150b18180b231a0b0c180c160b1e0b0a180a16160c240b160b1d180b241a0c190a190a04170b0317010b04340c1407010c250a253c000c220a22370038000c1a0a2236000b1438050c1f0b250b1f38200c21380a0c280b0704ac010b210600000000000000000b280b1934380b0c0f0c1005b4010b280b19340b21060000000000000000380c0c100c0f0b100b0f0c200c290a2236000b2038010b1a0b02160c1b0b22370038000c1c0a1c0a1b2404c90105cd010b1b0b1c17270b29380d023e010401003cc8010b050406381a0c0e0c0d050d381b0c250c280b250b280c0e0c0d0b0d0b0e0c190c180b060418380e0c110c100c0f0522380f0c2a0c260c290b260b290b2a0c110c100c0f0b0f0b100b11010c13350b13350c1e0c1d0a04320000000000000000000000000000000021047a0a190a0c180a0a1a0a1e160c140a180a1e180a141a0c120a1d0a19180a0c180a0a1a0b141a0c140a120a14180a0b180a091a0c2b0a120a14180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c270a270a2b23047205610a270c2b0a120a14180a0b180a091a0a271a0b271632020000000000000000000000000000001a0c27055c0b2b0b12170a0b180a091a0c040a040b0b180c160b180b09180a16160c220b160b19180b221a0b0c180c170b1e0b0a180a17160c230b170b1d180b231a0a04170b0317010b04340c1507010c240a243c000c210a21370038000c1a0a2136000b1538050c1f0b240b1f382038100c200a2136000b2038010b1a0b02160c1b0b21370038000c1c0a1c0a1b2404c30105c7010b1b0b1c1727023f010401003dc1010b050406381a0c0e0c0d050d381b0c230c260b230b260c0e0c0d0b0d0b0e0c170c160b06041738120c110c10051e38130c240c270b240c100b270c110b10350b11350c1c0c1b0a0432000000000000000000000000000000002104720a170a0c180a0a1a0a1c160c120a160a1c180a121a0c0f0a1b0a17180a0c180a0a1a0b121a0c120a0f0a12180a0b180a091a0c280a0f0a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c250a250a2823046a05590a250c280a0f0a12180a0b180a091a0a251a0b251632020000000000000000000000000000001a0c2505540b280b0f170a0b180a091a0c040a040b0b180c140b160b09180a14160c200b140b17180b201a0b0c180c150b1c0b0a180a15160c210b150b1b180b211a0a04170b0317010b04340c1307010c220a223c000c1f0a1f370038000c180a1f36000b1338050c1d0b220b1d382006000000000000000038140c1e0a1f36000b1e38010b180b02160c190b1f370038000c1a0a1a0a192404bc0105c0010b190b1a17270240010401003ed7010b050406381a0c0e0c0d050d381b0c260c2a0b260b2a0c0e0c0d0b0d0b0e0c180c170b06041a3815353816350c100c0f05203817353818350c100c0f0b0f0b100c1d0c1c0a0432000000000000000000000000000000002104720a180a0c180a0a1a0a1d160c120a170a1d180a121a0c110a1c0a18180a0c180a0a1a0b121a0c120a110a12180a0b180a091a0c2b0a110a12180a0b180a091a32020000000000000000000000000000001a3201000000000000000000000000000000160c290a290a2b23046a05590a290c2b0a110a12180a0b180a091a0a291a0b291632020000000000000000000000000000001a0c2905540b2b0b11170a0b180a091a0c040a040b0b180c150b170b09180a15160c220b150b18180b221a0c130a130b0c180c160b1d0b0a180a16160c230b160b1c180b231a0a04170b0317010b04340c1407010c240a243c000c210a21370038000c190a2136000b1438050c1e0a240b1e38200c20380a0c270b240d200d270b13340600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381901010b270c250b200c280b250c1f0a2136000b1f38010b190b02160c1a0b21370038000c1b0a1b0a1a2404d00105d4010b1a0b1b17270b28380d02410300003f070b0139000c020b000b023f0002420104003f140a00116a0701210406050b0b0001060000000000000000116b270a000b01382139000c020b000b023f0002430104003f120a00116a0701210406050b0b0001060000000000000000116b27380a39000c010b000b013f000244010400010206ffffffffffffffff2745010400010206ffffffffffffffff27460104000102060000000000000000274701040100415b0b00116a0c030a030701210408050b060000000000000000116b270a033c000c070a0736000b0138050c0a0e0a38000c04116d0101010c0e38040c0d0c0c0b04350610270000000000000b0e1735180c050a050b0d35180c0b0b0c353210270000000000000000000000000000180b05160c090b0b0b091a340c060a060b0226043c05400b07010700273822044a0b0a06000000000000000038060b06380c0c080c0f055138060b060b0a060000000000000000380b0c0f0c080b0736000b0f38010b033c0136010b0838090248010400010206000000000000000027490104000102060000000000000000274a010401004493010b00116a0c040a040701210408050b060000000000000000116b270b03044f38020c140c1238230c0d0c0f0a01350a0d0b0f1735180c050b120b0d35180a05160c0b0b050b14180b0b1a0c070a070b023524042e05300700270a043c000c090a0936000b0138050b073806320000000000000000000000000000000038070c1c0c180c1a0c160d160b1838010d1a0b1c38090b0936000b1638010b043c0136010b1a380905920138020c130c1538230c0e0c100a01350a0e0b101735180c060b130b0e35180a06160c0c0b060b15180b0c1a0c080a080b023524047005720700270a043c010c0a0a0a36010b0138240c11380a32000000000000000000000000000000000b110b0838070c1d0c190c1b0c170d170b1938010d1b0b1d38090b0a36010b1b38090b043c0036000b173801024b010400010206ffffffffffffffff274c0104010045650b00116a0c0d0a0d0701210408050b060000000000000000116b270b08041138120c0b0c0a051838130c160c170b160c0a0b170c0b0b0a350b0b350c100c0f0b030b07180c0e0b0f0b06180a0e160c140b0e0b10180b141a340c0c0a0c0a0526043405380b050b0c17270a0d3c0036000b0438050c150b0904480b1506000000000000000038060b0c38250c130c18054f38060b0c0b1506000000000000000038260c180c130b180b130c1238110e1238270c110a110a0524045b055f0b050b1117270b0d3c0136010b123809024d0104010046650b00116a0c0d0a0d0701210408050b060000000000000000116b270b0804143815353816350c0b0c0a051a3817353818350c0b0c0a0b0a0b0b0c100c0f0b030b07180c0e0b0f0b06180a0e160c140b0e0b10180b141a340c0c0a0c0a0526043405380b050b0c17270a0d3c0036000a0438050c1238060c160a0d0d120d160b040600000000000000000932000000000000000000000000000000003200000000000000000000000000000000381f01010b160c150b120c170b150c130e1338270c110a110a05240459055d0b050b1117270b0d3c0136010b1338090b173811024e0104010047590b00116a0c0f0a0f0701210408050b060000000000000000116b270b080412380e0c0c0c0b0c0a051c380f0c180c160c170b160b170b180c0c0c0b0c0a0b0a0b0b0b0c010c0d350b0d350c120c110b030b07180c100b110b06180a10160c150b100b12180b151a340c0e0a0e0a0526043c05400b050b0e17270a0f3c0036000b043805381d0c140e1438270c130a130a0524044f05530b050b1317270b0f3c0136010b143809024f03000100030a0b000b0138210c0207013c0036000b02380102500004010026170a00116a0c020a020701210408050d0b0001060000000000000000116b270b000b0138210c030b023c0036000b033801025103000100260c07010c010a013c0036000b0038050c020b010b02382802520004010026150b00116a0c020a020701210408050b060000000000000000116b270a023c0036000b0138050c030b020b033828025303000100030a07013c0036000b0138050c020b000b02382802540004010026150b00116a0c030a030701210408050b060000000000000000116b270b033c0036000b0238050c040b010b043828025503000100010607013c0036000b003805025603000100010507013c0036003829025703000100491707013c000c030a03370038000c020a000b022404110b0336000b0038050c0105150b03360038290c010b01020000000f001700"; - let res = run_binary_test("sample_coin_store", code); - assert!(res.is_ok(), "{:?}", res) -} - -#[test] -fn sample_liquidity_pool() { - let code = "a11ceb0b050000000e0100240224ab0103cf01b6030485056405e905eb0507d40bde0d08b2196006921ab80110ca1bd8070aa223ef010b9125160ca725c9120df0372a0e9a38280000010101020103010401050106000700080009000a000b000c000d000e000f021002110012080300010001000100130003000100010001001406030001000100010015060300010001000100160803000100010001001706030001000100010018060300010001000100190800001a0603000100010001001b0603000100010001001c0603000100010001001d06030001000100010220040100010d210003000100010001033804010601024f05010001025105010001015b0600105e0700027305010001057407001185010700001e00010100001f01010300000000220203030000000023040503000000002401060300000000250107030000000026010403000000002701040300000000280107030000000029010403000000002a010403000000002b080100002c010903000000002d010903000000002e030203000000002f0a0b01000030050103000000003108010300000000320c010300000000330c010300000000340d010300000000350e030300000000360d0103000000085f010901000f6011120010611314000862010901001063151200106407120010651612000766010902000002671c0701000768011501000e691e0700026a1f200100022221010100036b230101060a6c010100046d0828000c6e0827000b2b0801000a2b0801000e6f0415000e7015070002712a010100022e2b2001000e722e070007750101010008760101010001773334000778013503000000022b36370100027938010100027a011401000e7b140700027c012001000b7d010701000b7e0107000931080103000000017f083a01060b80010128000b81010701000b82010701000983014101030000000684010107001186010444001187014415000e88010b150017101a101e1a01181f1b201b1f101f1d2210221d1618231b2422051808182c102c1d2d1b242c0f300030141824312f102f1d30303218331b341b17303510351d3710371d38303a183b393b2c3b223b3b3b313b3c3b3d3b3e2439243e243d3f18243b243c0603030404040400010b0c010b0d03090009010902020b0c0109000b0c010901020303030b0c0109000b0c0109010b010309000901090203040403010301060c010105030303030302040402060c0303070b04030900090109020303040b0c010900030b0c0109010307020208120812081204081201090004040304030108120206081206081201020104020812081201060b0403090009010902030900090109020b0103070b000309000901090204070b04030900090109020b0c01090003030b0c01090103030209000901010b0d0309000901090201060b0c0109000109010304040402070b0c01090003010b0c010900020b0c010900060b10010900010b050309000901090202070b0e01090009000601070b040309000901090203030b0c0109000b0c01090104030404060b040309000901090203060b0403090009010902030301081101050e0303070b0003090009010902030b0c010b0d0309000901090204070b04030900090109020303030303030302070b0c0109000b0c0109000203060b0f010900010b030309000901090206040404040404030303030b01070b0003090009010902070b04030900090109020303040303030403010902010b02030900090109020b0b00030900090109020b10010b0d030900090109020b13010b0d030900090109020b0f010b0d03090009010902081408140b04030900090109020c060807030301060811010c020814081405060c081408140201030b100109000b130109000b0f010900010b13010900010b0803090009010902010b0e010900010b0903090009010902010b0603090009010902010b0b03090009010902010b0a0309000901090202070b0003090009010902070b040309000901090208030303030b0c010900030b0c0109010303050b0c0109000b0c010901100103030303070b0e010b0903090009010902070b0003090009010902070b04030900090109020304030b0c0109000304030b0c01090108010103070b0003090009010902030404040108150e6c69717569646974795f706f6f6c076163636f756e7404636f696e056576656e74067369676e657206737472696e670974696d657374616d700b636f696e5f68656c706572066375727665730b64616f5f73746f7261676509656d657267656e63790d676c6f62616c5f636f6e6669670a6c705f6163636f756e74076c705f636f696e046d6174680c737461626c655f6375727665047532353607757136347836340b4576656e747353746f726509466c6173686c6f616e0e466c6173686c6f616e4576656e74134c697175696469747941646465644576656e740d4c6971756964697479506f6f6c154c697175696469747952656d6f7665644576656e74124f7261636c65557064617465644576656e7415506f6f6c4163636f756e744361706162696c69747910506f6f6c437265617465644576656e7409537761704576656e741155706461746544414f4665654576656e740e5570646174654665654576656e741c6173736572745f6c705f76616c75655f69735f696e63726561736564146173736572745f706f6f6c5f756e6c6f636b656404436f696e024c50046275726e09666c6173686c6f616e156765745f63756d756c61746976655f7072696365730b6765745f64616f5f666565136765745f64616f5f666565735f636f6e666967136765745f646563696d616c735f7363616c6573076765745f6665650f6765745f666565735f636f6e666967116765745f72657365727665735f73697a650a696e697469616c697a650e69735f706f6f6c5f6578697374730e69735f706f6f6c5f6c6f636b6564046d696e741e6e65775f72657365727665735f61667465725f666565735f7363616c65640d7061795f666c6173686c6f616e0872656769737465720b7365745f64616f5f666565077365745f6665651073706c69745f6665655f746f5f64616f04737761700d7570646174655f6f7261636c6513706f6f6c5f637265617465645f68616e646c650b4576656e7448616e646c65166c69717569646974795f61646465645f68616e646c65186c69717569646974795f72656d6f7665645f68616e646c650b737761705f68616e646c6510666c6173686c6f616e5f68616e646c65156f7261636c655f757064617465645f68616e646c65117570646174655f6665655f68616e646c65157570646174655f64616f5f6665655f68616e646c6506785f6c6f616e06795f6c6f616e04785f696e05785f6f757404795f696e05795f6f75740b61646465645f785f76616c0b61646465645f795f76616c126c705f746f6b656e735f72656365697665640e636f696e5f785f726573657276650e636f696e5f795f72657365727665146c6173745f626c6f636b5f74696d657374616d70176c6173745f70726963655f785f63756d756c6174697665176c6173745f70726963655f795f63756d756c61746976650b6c705f6d696e745f6361700e4d696e744361706162696c6974790b6c705f6275726e5f6361700e4275726e4361706162696c69747907785f7363616c6507795f7363616c65066c6f636b6564036665650764616f5f6665650e72657475726e65645f785f76616c0e72657475726e65645f795f76616c106c705f746f6b656e735f6275726e65640a7369676e65725f636170105369676e65724361706162696c6974790763726561746f72076e65775f66656504553235360969735f737461626c65086c705f76616c756507636f6d706172650f69735f756e636f7272656c617465640966726f6d5f753132380866726f6d5f753634036d756c0969735f736f727465640576616c756506737570706c790c6d756c5f6469765f7531323807657874726163740a656d69745f6576656e74136173736572745f6e6f5f656d657267656e63790a616464726573735f6f661372657472696576655f7369676e65725f6361700b6d756c5f746f5f753132380473717274056d65726765076d756c5f64697610467265657a654361706162696c69747906537472696e670e6173736572745f69735f636f696e126173736572745f76616c69645f63757276651d6372656174655f7369676e65725f776974685f6361706162696c6974791b67656e65726174655f6c705f6e616d655f616e645f73796d626f6c1264657374726f795f667265657a655f63617008646563696d616c7306706f775f3130047a65726f0f6765745f64656661756c745f666565136765745f64656661756c745f64616f5f666565106e65775f6576656e745f68616e646c650d6765745f6665655f61646d696e146173736572745f76616c69645f64616f5f666565106173736572745f76616c69645f666565076465706f7369740b6e6f775f7365636f6e64730755513634783634086672616374696f6e07746f5f753132380c6f766572666c6f775f61646449bcc662ad23bb4f134aad8c8117c03f238bf3f03f324afe91ce231a559e8a6b00000000000000000000000000000000000000000000000000000000000000014e9fce03284c0ce0b86c88dd5a46f050cad2f4f33c4cdd29d98f501868558c81030864000000000000000308680000000000000003086e0000000000000003086a000000000000000308690000000000000003087000000000000000030866000000000000000308670000000000000003086d0000000000000003086b000000000000000308650000000000000003086f0000000000000003086c00000000000000030810270000000000000308e803000000000000052049bcc662ad23bb4f134aad8c8117c03f238bf3f03f324afe91ce231a559e8a6b126170746f733a3a6d657461646174615f7630c3070d6400000000000000174552525f57524f4e475f504149525f4f52444552494e47335768656e20636f696e73207573656420746f20637265617465207061697220686176652077726f6e67206f72646572696e672e6500000000000000184552525f504f4f4c5f4558495354535f464f525f50414952245768656e207061697220616c726561647920657869737473206f6e206163636f756e742e6600000000000000204552525f4e4f545f454e4f5547485f494e495449414c5f4c4951554944495459215768656e206e6f7420656e6f756768206c6971756964697479206d696e7465642e6700000000000000184552525f4e4f545f454e4f5547485f4c4951554944495459215768656e206e6f7420656e6f756768206c6971756964697479206d696e7465642e6800000000000000114552525f454d5054595f434f494e5f494e335768656e20626f7468205820616e6420592070726f766964656420666f7220737761702061726520657175616c207a65726f2e6900000000000000124552525f494e434f52524543545f535741504b5768656e20696e636f727265637420494e732f4f55547320617267756d656e74732070617373656420647572696e67207377617020616e64206d61746820646f65736e277420776f726b2e6a00000000000000194552525f494e434f52524543545f4255524e5f56414c5545531d496e636f7272656374206c7020636f696e206275726e2076616c7565736b00000000000000174552525f504f4f4c5f444f45535f4e4f545f4558495354225768656e20706f6f6c20646f65736e27742065786973747320666f7220706169722e6c000000000000000f4552525f554e524541434841424c451353686f756c64206e65766572206f636375722e6d00000000000000284552525f4e4f545f454e4f5547485f5045524d495353494f4e535f544f5f494e495449414c495a45525768656e2060696e697469616c697a65282960207472616e73616374696f6e206973207369676e6564207769746820616e79206163636f756e74206f74686572207468616e20406c6971756964737761702e6e00000000000000134552525f454d5054595f434f494e5f4c4f414e385768656e20626f7468205820616e6420592070726f766964656420666f7220666c6173686c6f616e2061726520657175616c207a65726f2e6f00000000000000124552525f504f4f4c5f49535f4c4f434b4544145768656e20706f6f6c206973206c6f636b65642e70000000000000000d4552525f4e4f545f41444d494e165768656e2075736572206973206e6f742061646d696e000208370b0e010b0803090009010902390b0e010b03030900090109023a0b0e010b05030900090109023b0b0e010b09030900090109023c0b0e010b02030900090109023d0b0e010b06030900090109023e0b0e010b0b030900090109023f0b0e010b0a0309000901090201020240034103020204420343034403450303020346034703480304020c490b0c0109004a0b0c0109014b034c044d044e0b0f010b0d03090009010902500b10010b0d03090009010902520353035401550356030502035703580359030602024c044d040702015a08110802015c0509020442034303440345030a02015d030b02015d0304180018051801180318021808180a180b1809180618000000000f3c38000303051a0b020a000b030a0111180c0a0b040b000b050b0111180c080e080e0a11190c060b063102210319070427053b3801031d05390b020b03180c0b0b0b111b070d070d18111c111d0c0c0b04111b0b05111b111d0c090e090e0c11190c070b07310221033b070427070c27020100000104170c070f3d000c000b003700140921030b070b2702020100020004195938020304070027070f3b00030907092738030e0038040c02070f3c000c0538050c040a05370138060c070a05370238070c0a0a02350a07350a0411210c080a02350a0a350b0411210c0b0a0806000000000000000024032d05320a0b060000000000000000240c010534090c010b01033a0b05010703270a0536010a0838080c060a0536020a0b38090c090a050b070b0a380a0b000b053703380b070f3c010c030b0336040b080b0b0b023902380c0b060b0902030100020004243e112538020305070027070f3b00030a07092738030a000600000000000000002403100513080c0205170a01060000000000000000240c020b02031b070227070f3c000c030a03370138060c040a03370238070c050a0336010a0038080c060a0336020a0138090c07080a033600150b030b040b05380a0b060b070b000b013903020401000104251e112538020305070027070f3b00030a0709273803070f3d000c030a033705140c010a033706140c020b033707140c000b010b020b00020501000104171038020304070027070f3b000309070927070f3d000c000b003708140206010001040103380d0700020701000104171338020304070027070f3b000309070927070f3d000c000a003709140b00370a14020801000104171038020304070027070f3b000309070927070f3d000c000b00370b140209010001040103380e070d020a010001042619112538020305070027070f3b00030a0709273803070f3d000c000a00370138060c010b00370238070c020b010b02020b01040027150a001126070f2103090b00010708270a0011270c010a000b0112072d070a0011280b001129020c010000010738020304070027070f3b00020d01000104171038020304070027070f3b000309070927070f3d000c000b00370014020e01000200042976112538020305070027070f3b00030a070927380338050c07070f3c000c080a08370138060c0c0a08370238070c0f0e0038060c0b0e0138070c0e0a07320000000000000000000000000000000021032305350a0b0a0e112a112b0c050a05070e2403300b08010706270b05070e170c03054f0a0b350a070a0c3511210c0a0a0e350b070a0f3511210c0d0a0a0a0d230348054b0b0a0c02054d0b0d0c020b020c030b030c090a090600000000000000002403590b08010707270a0836010b00380f0a0836020b0138100a090a08370c38110c060b080b0c0b0f380a070f3c010c040b04360d0b0b0b0e0b09390438120b06020f0000002d3b38010303050c0b00070d112a0b020a04112a170c06051a38000310070c270b000b020a04070d112e17350c050b050c060b060c093801031f05280b01070d112a0b030b04112a170c0805363800032c070c270b010b030b04070d112e17350c070b070c080b080c0a0b090b0a021001000200042f6e112538020305070027070f3b00030a0709270b023a030c0b0c070e0038060c060e0138070c0a0a06060000000000000000240319051c080c0305200a0a060000000000000000240c030b030324070127070f3c000c050a05370138060c090a05370238070c0d0b090a07160c090b0d0a0b160c0d0a0536010b00380f0a0536020b0138100a05370138060a05370238070a060a0a0a05370b1438130c0c0c080a053709140a05370a140b09350b0d350b080b0c38140a050a060a0a3815090b05360015070f3c010c040b04360e0b060b070b0a0b0b390538160211010001073263112538173818380203090b00010700273819070f3b002003120b0001070a27070f2b070c090b09100f11310c08381a0c060c050e080b050b06310608381b0c040c030c020b03381c0600000000000000000c0a0600000000000000000c0b381d032e0534381e11360c0a381f11360c0b38203821060000000000000000320000000000000000000000000000000032000000000000000000000000000000000b040b020b0a0b0b093822113939000c070e080b073f000e0838230e0838240e0838250e0838260e0838270e0838280e0838290e08382a0e08382b39010c010d01360f0b0011263906382c0e080b013f01021201040200043f27380203060b0001070027070f3b00030d0b000107092738030b001126113c2103150705270a01113d070f3c000c030a010b03360815070f3c010c020b0236100b013907382d021301040200043f27380203060b0001070027070f3b00030d0b000107092738030b001126113c2103150705270a01113e070f3c000c030a010b03360b15070f3c010c020b0236110b013908382e0214000000403b0a00370b140c0a0a003708140c040a0a0a0418070019060000000000000000220311051a0b0a0b041807001a060100000000000000160c0305200b0a0b041807001a0c030b030c050b010a05070d112e0c060b020b05070d112e0c080a0036010b0638080c070b0036020b0838090c09070f0b070b09382f02150100020004427b112538020305070027070f3b00030a07092738030e0038060c0c0e0238070c100a0c0600000000000000002403160519080c04051d0a10060000000000000000240c040b040321070127070f3c000c0b0a0b370138060c0e0a0b370238070c120a0b36010b00380f0a0b36020b0238100a0b36010a0138080c0f0a0b36020a0338090c130a0b370138060a0b370238070a0c0a100a0b370b1438130c110c0d0a0b3709140a0b370a140a0e350a12350b0d350b113538140a0b0a0c0a1038150b0b0b0e0b12380a070f3c010c0a0b0a36120c090b0c0c050b100c060b010c070b030c080b090b050b070b060b08390938300b0f0b1302160000010043540a003707140c0711400c050a050b0717350c0a0a0a320000000000000000000000000000000024031005150a01060000000000000000220c030517090c030b03031a051f0a02060000000000000000220c040521090c040b040324054f0a020a01114111420a0a180c080b010b02114111420b0a180c090a003705140b0811430a003605150a003706140b0911430a00360615070f3c010c060b0636130a003705140a00370614390a38310b050b003607150204090400040104060002040304040402040b04070408040a04050001000407000000000700060003000500180118021803180418051806180718081809180a180b180c180d180e181018111812181318141800"; - let res = run_binary_test("sample_liquidity_pool", code); - assert!(res.is_ok(), "{:?}", res) -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.proptest-regressions b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.proptest-regressions deleted file mode 100644 index 3945bd370c9..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.proptest-regressions +++ /dev/null @@ -1,10 +0,0 @@ -# Seeds for failure cases proptest has generated in the past. It is -# automatically read and these particular cases re-run before any -# novel cases are generated. -# -# It is recommended to check this file in to source control so that -# everyone who runs the test benefits from these saved cases. -cc 2beb0a0e65962432af560e626fa109d269b07db8807968413425f0bb14bb3667 # shrinks to module = CompiledModule: { datatype_handles: [ DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false }, DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false },] function_handles: [ FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(0) }, FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(1) },] struct_defs: [ StructDefinition { struct_handle: 1, access: 0x4, field_count: 0, fields: 0 },] field_defs: [] function_defs: [ FunctionDefinition { function: 1, access: 0x2, code: CodeUnit { max_stack_size: 0, locals: 0 code: [] } },] type_signatures: [ TypeSignature(Unit),] function_signatures: [ FunctionSignature { return_type: Unit, arg_types: [] }, FunctionSignature { return_type: Unit, arg_types: [] },] locals_signatures: [ LocalsSignature([]),] string_pool: [ "",] address_pool: [ Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),] } -cc c14ae393a6eefae82c0f4ede2acaa0aa0e993c1bba3fe3e5958e6e31cb5d2957 # shrinks to module = CompiledModule: { module_handles: [ ModuleHandle { address: AddressPoolIndex(0), name: IdentifierIndex(0) },] datatype_handles: [ DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false }, DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false },] function_handles: [ FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(0) }, FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(1) },] struct_defs: [ StructDefinition { struct_handle: 1, access: 0x4, field_count: 0, fields: 0 },] field_defs: [] function_defs: [ FunctionDefinition { function: 1, access: 0x2, code: CodeUnit { max_stack_size: 0, locals: 0 code: [] } },] type_signatures: [ TypeSignature(Unit),] function_signatures: [ FunctionSignature { return_type: Unit, arg_types: [] }, FunctionSignature { return_type: Unit, arg_types: [] },] locals_signatures: [ LocalsSignature([]),] string_pool: [ "",] address_pool: [ Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),] } , oob_mutations = [] -cc 88615e15ef42d29405cd91d6d0a573ccbeb833d0c7471f718ee794bc5ba399ca # shrinks to module = CompiledModule: { module_handles: [ ModuleHandle { address: AddressPoolIndex(0), name: IdentifierIndex(0) },] datatype_handles: [ DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false }, DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false }, DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false },] function_handles: [ FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(0) }, FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(1) },] struct_defs: [ StructDefinition { struct_handle: 1, access: 0x4, field_count: 0, fields: 0 }, StructDefinition { struct_handle: 2, access: 0x4, field_count: 0, fields: 0 },] field_defs: [] function_defs: [ FunctionDefinition { function: 1, access: 0x2, code: CodeUnit { max_stack_size: 0, locals: 0 code: [] } },] type_signatures: [ TypeSignature(Unit),] function_signatures: [ FunctionSignature { return_type: Unit, arg_types: [] }, FunctionSignature { return_type: Unit, arg_types: [] },] locals_signatures: [ LocalsSignature([]),] string_pool: [ "",] address_pool: [ Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),] } , oob_mutations = [OutOfBoundsMutation { src_kind: StructDefinition, src_idx: Index(0), dst_kind: FieldDefinition, offset: 0 }] -cc a34039f5d57751762a6eacf3ca3a2857781fb0bd0af0b7a06a9427f896f29aa9 # shrinks to module = CompiledModule: { module_handles: [ ModuleHandle { address: AddressPoolIndex(0), name: IdentifierIndex(0) },] datatype_handles: [ DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false }, DatatypeHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), is_resource: false },] function_handles: [ FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(0) }, FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), signature: FunctionSignatureIndex(1) },] struct_defs: [ StructDefinition { struct_handle: 1, access: 0x2, field_count: 0, fields: 0 },] field_defs: [] function_defs: [ FunctionDefinition { function: 1, access: 0x0, code: CodeUnit { max_stack_size: 0, locals: 0 code: [ BrTrue(1),] } },] type_signatures: [ TypeSignature(Unit), TypeSignature(Unit),] function_signatures: [ FunctionSignature { return_type: Unit, arg_types: [] }, FunctionSignature { return_type: Unit, arg_types: [] },] locals_signatures: [ LocalsSignature([]),] string_pool: [ "",] address_pool: [ Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),] } , oob_mutations = [] diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs deleted file mode 100644 index bdd3dd765f3..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::{check_bounds::BoundsChecker, file_format::*, file_format_common}; -use move_core_types::vm_status::StatusCode; - -#[test] -fn empty_module_no_errors() { - BoundsChecker::verify_module(&basic_test_module()).unwrap(); -} - -#[test] -fn invalid_default_module() { - BoundsChecker::verify_module(&CompiledModule { - version: file_format_common::VERSION_MAX, - ..Default::default() - }) - .unwrap_err(); -} - -#[test] -fn invalid_self_module_handle_index() { - let mut m = basic_test_module(); - m.self_module_handle_idx = ModuleHandleIndex(12); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_type_param_in_fn_return_() { - use SignatureToken::*; - - let mut m = basic_test_module(); - m.function_handles[0].return_ = SignatureIndex(1); - m.signatures.push(Signature(vec![TypeParameter(0)])); - assert_eq!(m.signatures.len(), 2); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_type_param_in_fn_parameters() { - use SignatureToken::*; - - let mut m = basic_test_module(); - m.function_handles[0].parameters = SignatureIndex(1); - m.signatures.push(Signature(vec![TypeParameter(0)])); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_struct_in_fn_return_() { - use SignatureToken::*; - - let mut m = basic_test_module(); - m.function_handles[0].return_ = SignatureIndex(1); - m.signatures - .push(Signature(vec![Datatype(DatatypeHandleIndex::new(1))])); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_type_param_in_field() { - use SignatureToken::*; - - let mut m = basic_test_module(); - match &mut m.struct_defs[0].field_information { - StructFieldInformation::Declared(ref mut fields) => { - fields[0].signature.0 = TypeParameter(0); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } - _ => panic!("attempt to change a field that does not exist"), - } -} - -#[test] -fn invalid_struct_in_field() { - use SignatureToken::*; - - let mut m = basic_test_module(); - match &mut m.struct_defs[0].field_information { - StructFieldInformation::Declared(ref mut fields) => { - fields[0].signature.0 = Datatype(DatatypeHandleIndex::new(3)); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } - _ => panic!("attempt to change a field that does not exist"), - } -} - -#[test] -fn invalid_struct_with_actuals_in_field() { - use SignatureToken::*; - - let mut m = basic_test_module(); - match &mut m.struct_defs[0].field_information { - StructFieldInformation::Declared(ref mut fields) => { - fields[0].signature.0 = DatatypeInstantiation(Box::new(( - DatatypeHandleIndex::new(0), - vec![TypeParameter(0)], - ))); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH - ); - } - _ => panic!("attempt to change a field that does not exist"), - } -} - -#[test] -fn invalid_locals_id_in_call() { - use Bytecode::*; - - let mut m = basic_test_module(); - m.function_instantiations.push(FunctionInstantiation { - handle: FunctionHandleIndex::new(0), - type_parameters: SignatureIndex::new(1), - }); - let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); - m.function_defs[0].code.as_mut().unwrap().code = vec![CallGeneric(func_inst_idx)]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_type_param_in_call() { - use Bytecode::*; - use SignatureToken::*; - - let mut m = basic_test_module(); - m.signatures.push(Signature(vec![TypeParameter(0)])); - m.function_instantiations.push(FunctionInstantiation { - handle: FunctionHandleIndex::new(0), - type_parameters: SignatureIndex::new(1), - }); - let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); - m.function_defs[0].code.as_mut().unwrap().code = vec![CallGeneric(func_inst_idx)]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_struct_as_type_actual_in_exists() { - use Bytecode::*; - use SignatureToken::*; - - let mut m = basic_test_module(); - m.signatures - .push(Signature(vec![Datatype(DatatypeHandleIndex::new(3))])); - m.function_instantiations.push(FunctionInstantiation { - handle: FunctionHandleIndex::new(0), - type_parameters: SignatureIndex::new(1), - }); - let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); - m.function_defs[0].code.as_mut().unwrap().code = vec![CallGeneric(func_inst_idx)]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_friend_module_address() { - let mut m = basic_test_module(); - m.friend_decls.push(ModuleHandle { - address: AddressIdentifierIndex::new(m.address_identifiers.len() as TableIndex), - name: IdentifierIndex::new(0), - }); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_friend_module_name() { - let mut m = basic_test_module(); - m.friend_decls.push(ModuleHandle { - address: AddressIdentifierIndex::new(0), - name: IdentifierIndex::new(m.identifiers.len() as TableIndex), - }); - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_signature_for_vector_operation() { - use Bytecode::*; - - let skeleton = basic_test_module(); - let sig_index = SignatureIndex(skeleton.signatures.len() as u16); - for bytecode in [ - VecPack(sig_index, 0), - VecLen(sig_index), - VecImmBorrow(sig_index), - VecMutBorrow(sig_index), - VecPushBack(sig_index), - VecPopBack(sig_index), - VecUnpack(sig_index, 0), - VecSwap(sig_index), - ] { - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![bytecode]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } -} - -#[test] -fn invalid_struct_for_vector_operation() { - use Bytecode::*; - use SignatureToken::*; - - let mut skeleton = basic_test_module(); - skeleton - .signatures - .push(Signature(vec![Datatype(DatatypeHandleIndex::new(3))])); - let sig_index = SignatureIndex((skeleton.signatures.len() - 1) as u16); - for bytecode in [ - VecPack(sig_index, 0), - VecLen(sig_index), - VecImmBorrow(sig_index), - VecMutBorrow(sig_index), - VecPushBack(sig_index), - VecPopBack(sig_index), - VecUnpack(sig_index, 0), - VecSwap(sig_index), - ] { - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![bytecode]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } -} - -#[test] -fn invalid_type_param_for_vector_operation() { - use Bytecode::*; - use SignatureToken::*; - - let mut skeleton = basic_test_module(); - skeleton.signatures.push(Signature(vec![TypeParameter(0)])); - let sig_index = SignatureIndex((skeleton.signatures.len() - 1) as u16); - for bytecode in [ - VecPack(sig_index, 0), - VecLen(sig_index), - VecImmBorrow(sig_index), - VecMutBorrow(sig_index), - VecPushBack(sig_index), - VecPopBack(sig_index), - VecUnpack(sig_index, 0), - VecSwap(sig_index), - ] { - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![bytecode]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } -} - -#[test] -fn invalid_variant_handle_index_for_enum_operation() { - use Bytecode::*; - - let skeleton = basic_test_module(); - let variant_handle_index = VariantHandleIndex(skeleton.variant_handles.len() as u16); - let variant_handle_inst_index = - VariantInstantiationHandleIndex(skeleton.variant_instantiation_handles.len() as u16); - for bytecode in [ - PackVariant(variant_handle_index), - UnpackVariant(variant_handle_index), - UnpackVariantImmRef(variant_handle_index), - UnpackVariantMutRef(variant_handle_index), - PackVariantGeneric(variant_handle_inst_index), - UnpackVariantGeneric(variant_handle_inst_index), - UnpackVariantGenericImmRef(variant_handle_inst_index), - UnpackVariantGenericMutRef(variant_handle_inst_index), - ] { - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![bytecode]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); - } -} - -#[test] -fn invalid_variant_jump_table_index() { - use Bytecode::*; - - let skeleton = basic_test_module(); - let jt_index = VariantJumpTableIndex( - skeleton.function_defs[0] - .code - .as_ref() - .map(|c| c.jump_tables.len() as u16) - .unwrap_or(0u16), - ); - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![VariantSwitch(jt_index)]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} - -#[test] -fn invalid_variant_jump_table_code_offset() { - use Bytecode::*; - - let mut skeleton = basic_test_module_with_enum(); - let enum_index = EnumDefinitionIndex(0); - skeleton.function_defs[0].code.as_mut().unwrap().code = vec![LdU64(0), Pop, Ret]; - skeleton.function_defs[0].code.as_mut().unwrap().jump_tables = vec![VariantJumpTable { - head_enum: enum_index, - jump_table: JumpTableInner::Full(vec![100]), - }]; - - let jt_index = VariantJumpTableIndex( - skeleton.function_defs[0] - .code - .as_ref() - .map(|c| c.jump_tables.len() as u16) - .unwrap_or(0u16), - ); - let mut m = skeleton.clone(); - m.function_defs[0].code.as_mut().unwrap().code = vec![VariantSwitch(jt_index)]; - assert_eq!( - BoundsChecker::verify_module(&m).unwrap_err().major_status(), - StatusCode::INDEX_OUT_OF_BOUNDS - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/catch_unwind.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/catch_unwind.rs deleted file mode 100644 index 6b9fc629db0..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/catch_unwind.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::panic::{self, PanicInfo}; - -use fail::FailScenario; -use move_binary_format::file_format::empty_module; -use move_core_types::{ - state::{self, VMState}, - vm_status::StatusCode, -}; -use move_vm_config::verifier::VerifierConfig; - -#[ignore] -#[test] -fn test_unwind() { - let scenario = FailScenario::setup(); - fail::cfg("verifier-failpoint-panic", "panic").unwrap(); - - panic::set_hook(Box::new(move |_: &PanicInfo<'_>| { - assert_eq!(state::get_state(), VMState::VERIFIER); - })); - - let m = empty_module(); - let res = move_bytecode_verifier::verify_module_with_config(&VerifierConfig::unbounded(), &m) - .unwrap_err(); - assert_eq!(res.major_status(), StatusCode::VERIFIER_INVARIANT_VIOLATION); - scenario.teardown(); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs deleted file mode 100644 index b380560a91a..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::Bytecode; -use move_bytecode_verifier::{ability_cache::AbilityCache, code_unit_verifier}; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::vm_status::StatusCode; -use move_vm_config::verifier::VerifierConfig; - -use crate::support::dummy_procedure_module; - -#[test] -fn invalid_fallthrough_br_true() { - let module = &dummy_procedure_module(vec![Bytecode::LdFalse, Bytecode::BrTrue(1)]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -#[test] -fn invalid_fallthrough_br_false() { - let module = &dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::BrFalse(1)]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -// all non-branch instructions should trigger invalid fallthrough; just check -// one of them -#[test] -fn invalid_fallthrough_non_branch() { - let module = &dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::Pop]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -#[test] -fn valid_fallthrough_branch() { - let module = &dummy_procedure_module(vec![Bytecode::Branch(0)]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert!(result.is_ok()); -} - -#[test] -fn valid_fallthrough_ret() { - let module = &dummy_procedure_module(vec![Bytecode::Ret]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert!(result.is_ok()); -} - -#[test] -fn valid_fallthrough_abort() { - let module = &dummy_procedure_module(vec![Bytecode::LdU64(7), Bytecode::Abort]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert!(result.is_ok()); -} - -#[test] -fn test_max_number_of_bytecode() { - let mut nops = vec![]; - for _ in 0..u16::MAX - 1 { - nops.push(Bytecode::Nop); - } - nops.push(Bytecode::Ret); - let module = &dummy_procedure_module(nops); - let ability_cache = &mut AbilityCache::new(module); - - let result = code_unit_verifier::verify_module( - &VerifierConfig::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert!(result.is_ok()); -} - -#[test] -fn test_max_basic_blocks() { - let mut code = (0..17) - .map(|idx| Bytecode::Branch(idx + 1)) - .collect::>(); - code.push(Bytecode::Ret); - let module = &dummy_procedure_module(code); - let ability_cache = &mut AbilityCache::new(module); - - let result = code_unit_verifier::verify_module( - &VerifierConfig { - max_basic_blocks: Some(16), - ..Default::default() - }, - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::TOO_MANY_BASIC_BLOCKS - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/constants_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/constants_tests.rs deleted file mode 100644 index 1dc747b5af0..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/constants_tests.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{Constant, SignatureToken, empty_module}; -use move_bytecode_verifier::constants; -use move_core_types::vm_status::StatusCode; - -#[test] -fn valid_primitives() { - let mut module = empty_module(); - module.constant_pool = vec![ - Constant { - type_: SignatureToken::Bool, - data: vec![0], - }, - Constant { - type_: SignatureToken::U8, - data: vec![0], - }, - Constant { - type_: SignatureToken::U16, - data: vec![0, 0], - }, - Constant { - type_: SignatureToken::U32, - data: vec![0, 0, 0, 0], - }, - Constant { - type_: SignatureToken::U64, - data: vec![0, 0, 0, 0, 0, 0, 0, 0], - }, - Constant { - type_: SignatureToken::U128, - data: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - }, - Constant { - type_: SignatureToken::U256, - data: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - }, - Constant { - type_: SignatureToken::Address, - data: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - }, - ]; - assert!(constants::verify_module(&module).is_ok()); -} - -#[test] -fn invalid_primitives() { - malformed(SignatureToken::U8, vec![0, 0]); - malformed(SignatureToken::U16, vec![0, 0, 0, 0]); - malformed(SignatureToken::U32, vec![0, 0, 0]); - malformed(SignatureToken::U64, vec![0]); - malformed(SignatureToken::U128, vec![0]); - malformed(SignatureToken::U256, vec![0, 0]); - let data = vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ]; - malformed(SignatureToken::Address, data); -} - -#[test] -fn valid_vectors() { - let double_vec = |item: Vec| -> Vec { - let mut items = vec![2]; - items.extend(item.clone()); - items.extend(item); - items - }; - let large_vec = |item: Vec| -> Vec { - let mut items = vec![0xFF, 0xFF, 3]; - (0..0xFFFF).for_each(|_| items.extend(item.clone())); - items - }; - let mut module = empty_module(); - module.constant_pool = vec![ - // empty - Constant { - type_: tvec(SignatureToken::Bool), - data: vec![0], - }, - Constant { - type_: tvec(tvec(SignatureToken::Bool)), - data: vec![0], - }, - Constant { - type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))), - data: vec![0], - }, - Constant { - type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))), - data: double_vec(vec![0]), - }, - // small - Constant { - type_: tvec(SignatureToken::Bool), - data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1], - }, - Constant { - type_: tvec(SignatureToken::U8), - data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1], - }, - // large - Constant { - type_: tvec(SignatureToken::Bool), - data: large_vec(vec![0]), - }, - Constant { - type_: tvec(SignatureToken::U8), - data: large_vec(vec![0]), - }, - Constant { - type_: tvec(SignatureToken::U16), - data: large_vec(vec![0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U32), - data: large_vec(vec![0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U64), - data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U128), - data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U256), - data: large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ]), - }, - Constant { - type_: tvec(SignatureToken::Address), - data: large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ]), - }, - // double large - Constant { - type_: tvec(tvec(SignatureToken::Bool)), - data: double_vec(large_vec(vec![0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U8)), - data: double_vec(large_vec(vec![0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U16)), - data: double_vec(large_vec(vec![0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U32)), - data: double_vec(large_vec(vec![0, 0, 0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U64)), - data: double_vec(large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U128)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U256)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ])), - }, - Constant { - type_: tvec(tvec(SignatureToken::Address)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ])), - }, - ]; - assert!(constants::verify_module(&module).is_ok()); -} - -#[test] -fn invalid_vectors() { - let double_vec = |item: Vec| -> Vec { - let mut items = vec![2]; - items.extend(item.clone()); - items.extend(item); - items - }; - let too_large_vec = |item: Vec| -> Vec { - let mut items = vec![0xFF, 0xFF, 3]; - (0..(0xFFFF + 1)).for_each(|_| items.extend(item.clone())); - items - }; - // wrong inner - malformed(tvec(SignatureToken::U16), vec![1, 0]); - malformed(tvec(SignatureToken::U32), vec![1, 0]); - malformed(tvec(SignatureToken::U64), vec![1, 0]); - malformed(tvec(SignatureToken::Address), vec![ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - ]); - // wrong lens - malformed(tvec(SignatureToken::U8), vec![0, 0]); - malformed(tvec(SignatureToken::U8), vec![0, 1]); - malformed(tvec(SignatureToken::U8), vec![2, 1, 1, 1]); - malformed(tvec(tvec(SignatureToken::U8)), double_vec(vec![0, 0])); - // too large - malformed(tvec(SignatureToken::U8), too_large_vec(vec![0])); -} - -#[test] -fn invalid_types() { - invalid_type(SignatureToken::TypeParameter(0), vec![0]); - invalid_type(SignatureToken::TypeParameter(0xFA), vec![0]); - invalid_type(tvec(SignatureToken::TypeParameter(0)), vec![0]); - invalid_type(tvec(SignatureToken::TypeParameter(0xAF)), vec![0]); - - invalid_type(SignatureToken::Signer, vec![0]); - invalid_type(tvec(SignatureToken::Signer), vec![0]); - - // TODO cannot check structs are banned currently. This can be handled by IR - // and source lang tests - // invalid_type(SignatureToken::Datatype(DatatypeHandleIndex(0)), vec![0]); -} - -fn tvec(s: SignatureToken) -> SignatureToken { - SignatureToken::Vector(Box::new(s)) -} - -fn malformed(type_: SignatureToken, data: Vec) { - error(type_, data, StatusCode::MALFORMED_CONSTANT_DATA) -} - -fn invalid_type(type_: SignatureToken, data: Vec) { - error(type_, data, StatusCode::INVALID_CONSTANT_TYPE) -} - -fn error(type_: SignatureToken, data: Vec, code: StatusCode) { - let mut module = empty_module(); - module.constant_pool = vec![Constant { type_, data }]; - assert!( - constants::verify_module(&module) - .unwrap_err() - .major_status() - == code - ) -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/control_flow_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/control_flow_tests.rs deleted file mode 100644 index 2f5d9070bf1..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/control_flow_tests.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::{ - errors::PartialVMResult, - file_format::{Bytecode, CompiledModule, FunctionDefinitionIndex, TableIndex}, -}; -use move_bytecode_verifier::control_flow; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::vm_status::StatusCode; -use move_vm_config::verifier::VerifierConfig; - -use crate::support::dummy_procedure_module; - -fn verify_module(verifier_config: &VerifierConfig, module: &CompiledModule) -> PartialVMResult<()> { - for (idx, function_definition) in module - .function_defs() - .iter() - .enumerate() - .filter(|(_, def)| !def.is_native()) - { - let current_function = FunctionDefinitionIndex(idx as TableIndex); - let code = function_definition - .code - .as_ref() - .expect("unexpected native function"); - - control_flow::verify_function( - verifier_config, - module, - current_function, - function_definition, - code, - &mut DummyMeter, - )?; - } - Ok(()) -} - -//************************************************************************************************** -// Simple cases - Copied from code unit verifier -//************************************************************************************************** - -#[test] -fn empty_bytecode() { - let module = dummy_procedure_module(vec![]); - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::EMPTY_CODE_UNIT, - ); -} - -#[test] -fn empty_bytecode_v5() { - let mut module = dummy_procedure_module(vec![]); - module.version = 5; - - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::EMPTY_CODE_UNIT, - ); -} - -#[test] -fn invalid_fallthrough_br_true() { - let module = dummy_procedure_module(vec![Bytecode::LdFalse, Bytecode::BrTrue(1)]); - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -#[test] -fn invalid_fallthrough_br_false() { - let module = dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::BrFalse(1)]); - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -// all non-branch instructions should trigger invalid fallthrough; just check -// one of them -#[test] -fn invalid_fallthrough_non_branch() { - let module = dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::Pop]); - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_FALL_THROUGH - ); -} - -#[test] -fn valid_fallthrough_branch() { - let module = dummy_procedure_module(vec![Bytecode::Branch(0)]); - let result = verify_module(&Default::default(), &module); - assert!(result.is_ok()); -} - -#[test] -fn valid_fallthrough_ret() { - let module = dummy_procedure_module(vec![Bytecode::Ret]); - let result = verify_module(&Default::default(), &module); - assert!(result.is_ok()); -} - -#[test] -fn valid_fallthrough_abort() { - let module = dummy_procedure_module(vec![Bytecode::LdU64(7), Bytecode::Abort]); - let result = verify_module(&Default::default(), &module); - assert!(result.is_ok()); -} - -#[test] -fn nested_loops_max_depth() { - let module = dummy_procedure_module(vec![ - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::BrFalse(1), - Bytecode::BrFalse(0), - Bytecode::Ret, - ]); - let result = verify_module( - &VerifierConfig { - max_loop_depth: Some(2), - ..VerifierConfig::default() - }, - &module, - ); - assert!(result.is_ok()); -} - -#[test] -fn nested_loops_exceed_max_depth() { - let module = dummy_procedure_module(vec![ - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::BrFalse(2), - Bytecode::BrFalse(1), - Bytecode::BrFalse(0), - Bytecode::Ret, - ]); - let result = verify_module( - &VerifierConfig { - max_loop_depth: Some(2), - ..VerifierConfig::default() - }, - &module, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::LOOP_MAX_DEPTH_REACHED - ); -} - -#[test] -fn non_loop_backward_jump() { - let module = dummy_procedure_module(vec![ - Bytecode::Branch(2), - Bytecode::Ret, - Bytecode::Branch(1), - ]); - let result = verify_module(&Default::default(), &module); - assert!(result.is_ok()); -} - -#[test] -fn non_loop_backward_jump_v5() { - let mut module = dummy_procedure_module(vec![ - Bytecode::Branch(2), - Bytecode::Ret, - Bytecode::Branch(1), - ]); - - module.version = 5; - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_LOOP_SPLIT, - ); -} - -#[test] -fn irreducible_control_flow_graph() { - let module = dummy_procedure_module(vec![ - Bytecode::LdTrue, - Bytecode::BrTrue(3), - Bytecode::Nop, - Bytecode::LdFalse, - Bytecode::BrFalse(2), - Bytecode::Ret, - ]); - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_LOOP_SPLIT, - ); -} - -#[test] -fn nested_loop_break() { - let module = dummy_procedure_module(vec![ - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::Branch(7), - Bytecode::BrFalse(2), - Bytecode::BrFalse(1), - Bytecode::BrFalse(0), - Bytecode::Ret, - ]); - let result = verify_module(&Default::default(), &module); - assert!(result.is_ok()); -} - -#[test] -fn nested_loop_break_v5() { - let mut module = dummy_procedure_module(vec![ - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::LdFalse, - Bytecode::Branch(7), - Bytecode::BrFalse(2), - Bytecode::BrFalse(1), - Bytecode::BrFalse(0), - Bytecode::Ret, - ]); - - module.version = 5; - let result = verify_module(&Default::default(), &module); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::INVALID_LOOP_BREAK, - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/duplication_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/duplication_tests.rs deleted file mode 100644 index 5c40fa75adb..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/duplication_tests.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::*; -use move_bytecode_verifier::DuplicationChecker; - -#[test] -fn duplicated_friend_decls() { - let mut m = basic_test_module(); - let handle = ModuleHandle { - address: AddressIdentifierIndex::new(0), - name: IdentifierIndex::new(0), - }; - m.friend_decls.push(handle.clone()); - m.friend_decls.push(handle); - DuplicationChecker::verify_module(&m).unwrap_err(); -} - -#[test] -fn duplicated_variant_handles() { - let mut m = basic_test_module_with_enum(); - m.variant_handles.push(m.variant_handles[0].clone()); - DuplicationChecker::verify_module(&m).unwrap_err(); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/generic_ops_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/generic_ops_tests.rs deleted file mode 100644 index c45ad03375d..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/generic_ops_tests.rs +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::*; -use move_bytecode_verifier::InstructionConsistency; -use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode, -}; - -// Make a Module with 2 structs and 2 resources with one field each, and 2 -// functions. One of the struct/resource and one of the function is generic, the -// other "normal". Also make a test function whose body will be filled by given -// test cases. -fn make_module() -> CompiledModule { - CompiledModule { - version: move_binary_format::file_format_common::VERSION_MAX, - module_handles: vec![ - // only self module - ModuleHandle { - address: AddressIdentifierIndex(0), - name: IdentifierIndex(0), - }, - ], - self_module_handle_idx: ModuleHandleIndex(0), - identifiers: vec![ - Identifier::new("M").unwrap(), // Module name - Identifier::new("S").unwrap(), // Struct name - Identifier::new("GS").unwrap(), // Generic struct name - Identifier::new("R").unwrap(), // Resource name - Identifier::new("GR").unwrap(), // Generic resource name - Identifier::new("f").unwrap(), // Field name - Identifier::new("fn").unwrap(), // Function name - Identifier::new("g_fn").unwrap(), // Generic function name - Identifier::new("test_fn").unwrap(), // Test function name - ], - address_identifiers: vec![ - AccountAddress::ZERO, // Module address - ], - datatype_handles: vec![ - DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - abilities: AbilitySet::PRIMITIVES, - type_parameters: vec![], - }, - DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(2), - abilities: AbilitySet::PRIMITIVES, - type_parameters: vec![DatatypeTyParameter { - constraints: AbilitySet::PRIMITIVES, - is_phantom: false, - }], - }, - DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(3), - abilities: AbilitySet::EMPTY | Ability::Key, - type_parameters: vec![], - }, - DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(4), - abilities: AbilitySet::EMPTY | Ability::Key, - type_parameters: vec![DatatypeTyParameter { - constraints: AbilitySet::PRIMITIVES, - is_phantom: false, - }], - }, - ], - struct_defs: vec![ - // struct S { f: u64 } - StructDefinition { - struct_handle: DatatypeHandleIndex(0), - field_information: StructFieldInformation::Declared(vec![FieldDefinition { - name: IdentifierIndex(5), - signature: TypeSignature(SignatureToken::U64), - }]), - }, - // struct GS { f: T } - StructDefinition { - struct_handle: DatatypeHandleIndex(1), - field_information: StructFieldInformation::Declared(vec![FieldDefinition { - name: IdentifierIndex(5), - signature: TypeSignature(SignatureToken::TypeParameter(0)), - }]), - }, - // struct R has key { f: u64 } - StructDefinition { - struct_handle: DatatypeHandleIndex(2), - field_information: StructFieldInformation::Declared(vec![FieldDefinition { - name: IdentifierIndex(5), - signature: TypeSignature(SignatureToken::U64), - }]), - }, - // struct GR has key { f: T } - StructDefinition { - struct_handle: DatatypeHandleIndex(3), - field_information: StructFieldInformation::Declared(vec![FieldDefinition { - name: IdentifierIndex(5), - signature: TypeSignature(SignatureToken::TypeParameter(0)), - }]), - }, - ], - function_handles: vec![ - // fun fn() - FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(6), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }, - // fun g_fn() - FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(7), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![AbilitySet::EMPTY | Ability::Key], - }, - // fun test_fn(Sender) - FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(8), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }, - ], - function_defs: vec![ - // public fun fn() { return; } - FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Visibility::Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }, - // fun g_fn() { return; } - FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Visibility::Private, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }, - // fun test_fn() { ... } - tests will fill up the code - FunctionDefinition { - function: FunctionHandleIndex(2), - visibility: Visibility::Private, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![], - jump_tables: vec![], - }), - }, - ], - signatures: vec![ - Signature(vec![]), // void - Signature(vec![SignatureToken::Signer]), // Signer - ], - constant_pool: vec![ - // an address - Constant { - type_: SignatureToken::Address, - data: AccountAddress::random().to_vec(), - }, - ], - metadata: vec![], - field_handles: vec![], - friend_decls: vec![], - struct_def_instantiations: vec![], - function_instantiations: vec![], - field_instantiations: vec![], - enum_defs: vec![], - enum_def_instantiations: vec![], - variant_handles: vec![], - variant_instantiation_handles: vec![], - } -} - -#[test] -fn generic_call_to_non_generic_func() { - let mut module = make_module(); - // bogus `CallGeneric fn()` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::CallGeneric(FunctionInstantiationIndex(0)), - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module.function_instantiations.push(FunctionInstantiation { - handle: FunctionHandleIndex(0), - type_parameters: SignatureIndex(2), - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("CallGeneric to non generic function must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn non_generic_call_to_generic_func() { - let mut module = make_module(); - // bogus `Call g_fn()` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(1)), Bytecode::Ret], - jump_tables: vec![], - }); - let err = InstructionConsistency::verify_module(&module) - .expect_err("Call to generic function must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn generic_pack_on_non_generic_struct() { - let mut module = make_module(); - // bogus `PackGeneric S` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::PackGeneric(StructDefInstantiationIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module - .struct_def_instantiations - .push(StructDefInstantiation { - def: StructDefinitionIndex(0), - type_parameters: SignatureIndex(2), - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("PackGeneric to non generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn non_generic_pack_on_generic_struct() { - let mut module = make_module(); - // bogus `Pack GS` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::Pack(StructDefinitionIndex(1)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - let err = InstructionConsistency::verify_module(&module) - .expect_err("Pack to generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn generic_unpack_on_non_generic_struct() { - let mut module = make_module(); - // bogus `UnpackGeneric S` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::Pack(StructDefinitionIndex(0)), - Bytecode::UnpackGeneric(StructDefInstantiationIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module - .struct_def_instantiations - .push(StructDefInstantiation { - def: StructDefinitionIndex(0), - type_parameters: SignatureIndex(2), - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("UnpackGeneric to non generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn non_generic_unpack_on_generic_struct() { - let mut module = make_module(); - // bogus `Unpack GS` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::PackGeneric(StructDefInstantiationIndex(0)), - Bytecode::Unpack(StructDefinitionIndex(1)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module - .struct_def_instantiations - .push(StructDefInstantiation { - def: StructDefinitionIndex(1), - type_parameters: SignatureIndex(2), - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("Unpack to generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn generic_mut_borrow_field_on_non_generic_struct() { - let mut module = make_module(); - // bogus `MutBorrowFieldGeneric S.t` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::Pack(StructDefinitionIndex(0)), - Bytecode::MutBorrowFieldGeneric(FieldInstantiationIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module.field_instantiations.push(FieldInstantiation { - handle: FieldHandleIndex(0), - type_parameters: SignatureIndex(2), - }); - module.field_handles.push(FieldHandle { - owner: StructDefinitionIndex(0), - field: 0, - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("MutBorrowFieldGeneric to non generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn non_generic_mut_borrow_field_on_generic_struct() { - let mut module = make_module(); - // bogus `MutBorrowField GS.f` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::PackGeneric(StructDefInstantiationIndex(0)), - Bytecode::MutBorrowField(FieldHandleIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module - .struct_def_instantiations - .push(StructDefInstantiation { - def: StructDefinitionIndex(1), - type_parameters: SignatureIndex(2), - }); - module.field_handles.push(FieldHandle { - owner: StructDefinitionIndex(1), - field: 0, - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("MutBorrowField to generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn generic_borrow_field_on_non_generic_struct() { - let mut module = make_module(); - // bogus `ImmBorrowFieldGeneric S.f` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::Pack(StructDefinitionIndex(0)), - Bytecode::ImmBorrowFieldGeneric(FieldInstantiationIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module.field_instantiations.push(FieldInstantiation { - handle: FieldHandleIndex(0), - type_parameters: SignatureIndex(2), - }); - module.field_handles.push(FieldHandle { - owner: StructDefinitionIndex(0), - field: 0, - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("ImmBorrowFieldGeneric to non generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} - -#[test] -fn non_generic_borrow_field_on_generic_struct() { - let mut module = make_module(); - // bogus `ImmBorrowField GS.f` - module.function_defs[2].code = Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![ - Bytecode::LdU64(10), - Bytecode::PackGeneric(StructDefInstantiationIndex(0)), - Bytecode::ImmBorrowField(FieldHandleIndex(0)), - Bytecode::Pop, - Bytecode::Ret, - ], - jump_tables: vec![], - }); - module - .struct_def_instantiations - .push(StructDefInstantiation { - def: StructDefinitionIndex(1), - type_parameters: SignatureIndex(2), - }); - module.field_handles.push(FieldHandle { - owner: StructDefinitionIndex(1), - field: 0, - }); - module.signatures.push(Signature(vec![SignatureToken::U64])); - let err = InstructionConsistency::verify_module(&module) - .expect_err("ImmBorrowField to generic struct must fail"); - assert_eq!( - err.major_status(), - StatusCode::GENERIC_MEMBER_OPCODE_MISMATCH - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/large_type_test.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/large_type_test.rs deleted file mode 100644 index 3b565fa979b..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/large_type_test.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex, - IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, - Visibility::Public, -}; -use move_bytecode_verifier_meter::bound::BoundMeter; -use move_core_types::{identifier::Identifier, vm_status::StatusCode}; - -use crate::unit_tests::production_config; - -const NUM_LOCALS: u8 = 64; -const NUM_CALLS: u16 = 77; -const NUM_FUNCTIONS: u16 = 177; - -fn get_nested_vec_type(len: usize) -> SignatureToken { - let mut ret = SignatureToken::Bool; - for _ in 0..len { - ret = SignatureToken::Vector(Box::new(ret)); - } - ret -} - -#[test] -fn test_large_types() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-37qw-jfpw-8899 - let mut m = empty_module(); - - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::Reference(Box::new(get_nested_vec_type(64)))) - .take(NUM_LOCALS as usize) - .collect(), - )); - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(0)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // returns_vecs - m.identifiers.push(Identifier::new("returns_vecs").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - parameters: SignatureIndex(0), - return_: SignatureIndex(1), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(1)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // takes_and_returns_vecs - m.identifiers - .push(Identifier::new("takes_and_returns_vecs").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(2), - parameters: SignatureIndex(1), - return_: SignatureIndex(1), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(2), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(1)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // takes_vecs - m.identifiers.push(Identifier::new("takes_vecs").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(3), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(3), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // other fcts - for i in 0..NUM_FUNCTIONS { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 4), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i + 4), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![], - jump_tables: vec![], - }), - }); - - let code = &mut m.function_defs[i as usize + 4].code.as_mut().unwrap().code; - code.clear(); - code.push(Bytecode::Call(FunctionHandleIndex(1))); - for _ in 0..NUM_CALLS { - code.push(Bytecode::Call(FunctionHandleIndex(2))); - } - code.push(Bytecode::Call(FunctionHandleIndex(3))); - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_large_types", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED, - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/limit_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/limit_tests.rs deleted file mode 100644 index 271e1cf955e..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/limit_tests.rs +++ /dev/null @@ -1,827 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::*; -use move_bytecode_verifier::{limits::LimitsVerifier, verify_module_with_config_for_test}; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode, -}; -use move_vm_config::verifier::{VerifierConfig, DEFAULT_MAX_IDENTIFIER_LENGTH}; - -use crate::unit_tests::production_config; - -#[test] -fn test_function_handle_type_instantiation() { - let mut m = basic_test_module(); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: std::iter::repeat(AbilitySet::ALL).take(10).collect(), - }); - - assert_eq!( - LimitsVerifier::verify_module( - &VerifierConfig { - max_generic_instantiation_length: Some(9), - ..Default::default() - }, - &m - ) - .unwrap_err() - .major_status(), - StatusCode::TOO_MANY_TYPE_PARAMETERS - ); -} - -#[test] -fn test_struct_handle_type_instantiation() { - let mut m = basic_test_module(); - m.datatype_handles.push(DatatypeHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(0), - abilities: AbilitySet::ALL, - type_parameters: std::iter::repeat(DatatypeTyParameter { - constraints: AbilitySet::ALL, - is_phantom: false, - }) - .take(10) - .collect(), - }); - - assert_eq!( - LimitsVerifier::verify_module( - &VerifierConfig { - max_generic_instantiation_length: Some(9), - ..Default::default() - }, - &m - ) - .unwrap_err() - .major_status(), - StatusCode::TOO_MANY_TYPE_PARAMETERS - ); -} - -#[test] -fn test_function_handle_parameters() { - let mut m = basic_test_module(); - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::Bool).take(10).collect(), - )); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(0), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - - assert_eq!( - LimitsVerifier::verify_module( - &VerifierConfig { - max_function_parameters: Some(9), - ..Default::default() - }, - &m - ) - .unwrap_err() - .major_status(), - StatusCode::TOO_MANY_PARAMETERS - ); -} - -#[test] -fn big_vec_unpacks() { - const N_TYPE_PARAMS: usize = 16; - let mut st = SignatureToken::Vector(Box::new(SignatureToken::U8)); - let type_params = vec![st; N_TYPE_PARAMS]; - st = SignatureToken::DatatypeInstantiation(Box::new((DatatypeHandleIndex(0), type_params))); - const N_VEC_PUSH: u16 = 1000; - let mut code = vec![]; - // 1. CopyLoc: ... -> ... st - // 2. VecPack: ... st -> ... Vec - // 3. VecUnpack: ... Vec -> ... st, st, st, ... st - for _ in 0..N_VEC_PUSH { - code.push(Bytecode::CopyLoc(0)); - code.push(Bytecode::VecPack(SignatureIndex(1), 1)); - code.push(Bytecode::VecUnpack(SignatureIndex(1), 1 << 15)); - } - // 1. VecPack: ... st, st, st, ... st -> ... Vec - // 2. Pop: ... Vec -> ... - for _ in 0..N_VEC_PUSH { - code.push(Bytecode::VecPack(SignatureIndex(1), 1 << 15)); - code.push(Bytecode::Pop); - } - code.push(Bytecode::Ret); - let type_param_constraints = DatatypeTyParameter { - constraints: AbilitySet::EMPTY, - is_phantom: false, - }; - let module = CompiledModule { - version: 5, - self_module_handle_idx: ModuleHandleIndex(0), - module_handles: vec![ModuleHandle { - address: AddressIdentifierIndex(0), - name: IdentifierIndex(0), - }], - datatype_handles: vec![DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - abilities: AbilitySet::ALL, - type_parameters: vec![type_param_constraints; N_TYPE_PARAMS], - }], - function_handles: vec![FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }], - field_handles: vec![], - friend_decls: vec![], - struct_def_instantiations: vec![], - function_instantiations: vec![], - field_instantiations: vec![], - signatures: vec![Signature(vec![]), Signature(vec![st])], - identifiers: vec![ - Identifier::new("f").unwrap(), - Identifier::new("generic_struct").unwrap(), - ], - address_identifiers: vec![AccountAddress::ONE], - constant_pool: vec![], - metadata: vec![], - struct_defs: vec![StructDefinition { - struct_handle: DatatypeHandleIndex(0), - field_information: StructFieldInformation::Native, - }], - function_defs: vec![FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Visibility::Public, - is_entry: true, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code, - jump_tables: vec![], - }), - }], - enum_defs: vec![], - enum_def_instantiations: vec![], - variant_handles: vec![], - variant_instantiation_handles: vec![], - }; - - // save module and verify that it can ser/de - let mut mvbytes = vec![]; - module.serialize(&mut mvbytes).unwrap(); - let module = CompiledModule::deserialize_with_defaults(&mvbytes).unwrap(); - - let res = verify_module_with_config_for_test( - "big_vec_unpacks", - &VerifierConfig { - max_loop_depth: Some(5), - max_generic_instantiation_length: Some(32), - max_function_parameters: Some(128), - max_basic_blocks: Some(1024), - max_push_size: Some(10000), - ..Default::default() - }, - &module, - &mut DummyMeter, - ); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::VALUE_STACK_PUSH_OVERFLOW - ); -} - -const MAX_STRUCTS: usize = 200; -const MAX_FIELDS: usize = 30; -const MAX_FUNCTIONS: usize = 1000; - -#[test] -fn max_struct_test() { - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_fields_in_struct: Some(MAX_FIELDS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, 0); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - multi_struct(&mut module, 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS / 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_STRUCT_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_STRUCT_DEFINITIONS_REACHED, - ); -} - -#[test] -fn max_fields_test() { - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_fields_in_struct: Some(MAX_FIELDS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, 1); - multi_fields(&mut module, MAX_FIELDS / 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 10); - multi_fields(&mut module, MAX_FIELDS - 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 50); - multi_fields(&mut module, MAX_FIELDS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 100); - multi_fields(&mut module, MAX_FIELDS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, 2); - multi_fields(&mut module, MAX_FIELDS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, 50); - multi_fields_except_one(&mut module, 0, 2, MAX_FIELDS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, 20); - multi_fields_except_one(&mut module, 19, MAX_FIELDS, MAX_FIELDS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, 100); - multi_fields_except_one(&mut module, 50, 1, MAX_FIELDS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); -} - -#[test] -fn max_functions_test() { - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_fields_in_struct: Some(MAX_FIELDS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, 1); - multi_functions(&mut module, 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 10); - multi_functions(&mut module, MAX_FUNCTIONS / 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 5); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 5); - multi_functions(&mut module, MAX_FUNCTIONS - 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, 5); - multi_functions(&mut module, MAX_FUNCTIONS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FUNCTION_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_functions(&mut module, MAX_FUNCTIONS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FUNCTION_DEFINITIONS_REACHED, - ); -} - -#[test] -fn max_mixed_config_test() { - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_fields_in_struct: Some(MAX_FIELDS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - - let config = VerifierConfig { - max_function_definitions: None, - max_data_definitions: None, - max_fields_in_struct: None, - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, 1); - multi_fields(&mut module, 1); - multi_functions(&mut module, 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 2); - multi_fields(&mut module, MAX_FIELDS * 2); - multi_functions(&mut module, MAX_FUNCTIONS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS + 1); - multi_fields(&mut module, MAX_FIELDS + 1); - multi_functions(&mut module, MAX_FUNCTIONS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_fields_in_struct: Some(MAX_FIELDS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS + 10); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS * 3); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 2); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS + 1); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_STRUCT_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS * 2); - multi_functions(&mut module, MAX_FUNCTIONS * 3); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - - let config = VerifierConfig { - max_data_definitions: Some(MAX_STRUCTS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS + 1); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS * 3); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 2); - multi_fields(&mut module, MAX_FIELDS * 3); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_STRUCT_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS * 2); - multi_functions(&mut module, MAX_FUNCTIONS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FUNCTION_DEFINITIONS_REACHED, - ); - - let config = VerifierConfig { - max_fields_in_struct: Some(MAX_FIELDS), - max_function_definitions: Some(MAX_FUNCTIONS), - ..Default::default() - }; - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 3); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS + 1); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!(res, Ok(())); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS + 1); - multi_fields(&mut module, MAX_FIELDS * 3); - multi_functions(&mut module, MAX_FUNCTIONS); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FIELD_DEFINITIONS_REACHED, - ); - let mut module = leaf_module("M"); - multi_struct(&mut module, MAX_STRUCTS * 2); - multi_fields(&mut module, MAX_FIELDS); - multi_functions(&mut module, MAX_FUNCTIONS * 2); - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::MAX_FUNCTION_DEFINITIONS_REACHED, - ); -} - -#[test] -fn max_identifier_len() { - let (config, _) = production_config(); - let max_ident = "z".repeat( - config - .max_identifier_len - .unwrap_or(DEFAULT_MAX_IDENTIFIER_LENGTH) as usize, - ); - let good_module = leaf_module(&max_ident); - - let res = LimitsVerifier::verify_module(&config, &good_module); - assert!(res.is_ok()); - - let max_ident = "z".repeat( - (config - .max_identifier_len - .unwrap_or(DEFAULT_MAX_IDENTIFIER_LENGTH) as usize) - / 2, - ); - let good_module = leaf_module(&max_ident); - - let res = LimitsVerifier::verify_module(&config, &good_module); - assert!(res.is_ok()); - - let over_max_ident = "z".repeat( - 1 + config - .max_identifier_len - .unwrap_or(DEFAULT_MAX_IDENTIFIER_LENGTH) as usize, - ); - let bad_module = leaf_module(&over_max_ident); - let res = LimitsVerifier::verify_module(&config, &bad_module); - - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::IDENTIFIER_TOO_LONG, - ); - - let over_max_ident = "zx".repeat( - 1 + config - .max_identifier_len - .unwrap_or(DEFAULT_MAX_IDENTIFIER_LENGTH) as usize, - ); - let bad_module = leaf_module(&over_max_ident); - let res = LimitsVerifier::verify_module(&config, &bad_module); - - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::IDENTIFIER_TOO_LONG, - ); -} - -#[test] -fn max_vec_len() { - let config = VerifierConfig { - max_constant_vector_len: Some(0xFFFF - 1), - ..Default::default() - }; - let double_vec = |item: Vec| -> Vec { - let mut items = vec![2]; - items.extend(item.clone()); - items.extend(item); - items - }; - let large_vec = |item: Vec| -> Vec { - let mut items = vec![0xFF, 0xFF, 3]; - (0..0xFFFF).for_each(|_| items.extend(item.clone())); - items - }; - fn tvec(s: SignatureToken) -> SignatureToken { - SignatureToken::Vector(Box::new(s)) - } - - let mut module = empty_module(); - module.constant_pool = vec![Constant { - type_: tvec(SignatureToken::Bool), - data: large_vec(vec![0]), - }]; - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::TOO_MANY_VECTOR_ELEMENTS, - ); - - let mut module = empty_module(); - module.constant_pool = vec![Constant { - type_: tvec(SignatureToken::U256), - data: large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ]), - }]; - let res = LimitsVerifier::verify_module(&config, &module); - assert_eq!( - res.unwrap_err().major_status(), - StatusCode::TOO_MANY_VECTOR_ELEMENTS, - ); - - let config = VerifierConfig { - max_constant_vector_len: Some(0xFFFF), - ..Default::default() - }; - - let mut module = empty_module(); - module.constant_pool = vec![ - // empty - Constant { - type_: tvec(SignatureToken::Bool), - data: vec![0], - }, - Constant { - type_: tvec(tvec(SignatureToken::Bool)), - data: vec![0], - }, - Constant { - type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))), - data: vec![0], - }, - Constant { - type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))), - data: double_vec(vec![0]), - }, - // small - Constant { - type_: tvec(SignatureToken::Bool), - data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1], - }, - Constant { - type_: tvec(SignatureToken::U8), - data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1], - }, - // large - Constant { - type_: tvec(SignatureToken::Bool), - data: large_vec(vec![0]), - }, - Constant { - type_: tvec(SignatureToken::U8), - data: large_vec(vec![0]), - }, - Constant { - type_: tvec(SignatureToken::U16), - data: large_vec(vec![0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U32), - data: large_vec(vec![0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U64), - data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U128), - data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - }, - Constant { - type_: tvec(SignatureToken::U256), - data: large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ]), - }, - Constant { - type_: tvec(SignatureToken::Address), - data: large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ]), - }, - // double large - Constant { - type_: tvec(tvec(SignatureToken::Bool)), - data: double_vec(large_vec(vec![0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U8)), - data: double_vec(large_vec(vec![0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U16)), - data: double_vec(large_vec(vec![0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U32)), - data: double_vec(large_vec(vec![0, 0, 0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U64)), - data: double_vec(large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U128)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ])), - }, - Constant { - type_: tvec(tvec(SignatureToken::U256)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ])), - }, - Constant { - type_: tvec(tvec(SignatureToken::Address)), - data: double_vec(large_vec(vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ])), - }, - ]; - let res = LimitsVerifier::verify_module(&config, &module); - - assert!(res.is_ok()); -} - -fn multi_struct(module: &mut CompiledModule, count: usize) { - for i in 0..count { - module - .identifiers - .push(Identifier::new(format!("A_{}", i)).unwrap()); - module.datatype_handles.push(DatatypeHandle { - module: module.self_module_handle_idx, - name: IdentifierIndex((module.identifiers.len() - 1) as u16), - abilities: AbilitySet::EMPTY, - type_parameters: vec![], - }); - module.struct_defs.push(StructDefinition { - struct_handle: DatatypeHandleIndex((module.datatype_handles.len() - 1) as u16), - field_information: StructFieldInformation::Declared(vec![]), - }); - } -} - -fn multi_fields(module: &mut CompiledModule, count: usize) { - for def in &mut module.struct_defs { - let mut fields = vec![]; - for i in 0..count { - module - .identifiers - .push(Identifier::new(format!("f_{}", i)).unwrap()); - fields.push(FieldDefinition { - name: Default::default(), - signature: TypeSignature(SignatureToken::U8), - }); - } - def.field_information = StructFieldInformation::Declared(fields); - } -} - -fn multi_fields_except_one(module: &mut CompiledModule, idx: usize, count: usize, one: usize) { - for (struct_idx, def) in module.struct_defs.iter_mut().enumerate() { - let mut fields = vec![]; - let count = if struct_idx == idx { one } else { count }; - for i in 0..count { - module - .identifiers - .push(Identifier::new(format!("f_{}", i)).unwrap()); - fields.push(FieldDefinition { - name: Default::default(), - signature: TypeSignature(SignatureToken::U8), - }); - } - def.field_information = StructFieldInformation::Declared(fields); - } -} - -fn multi_functions(module: &mut CompiledModule, count: usize) { - module.signatures.push(Signature(vec![])); - for i in 0..count { - module - .identifiers - .push(Identifier::new(format!("func_{}", i)).unwrap()); - module.function_handles.push(FunctionHandle { - module: module.self_module_handle_idx, - name: IdentifierIndex((module.identifiers.len() - 1) as u16), - parameters: SignatureIndex((module.signatures.len() - 1) as u16), - return_: SignatureIndex((module.signatures.len() - 1) as u16), - type_parameters: vec![], - }); - module.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex((module.function_handles.len() - 1) as u16), - visibility: Visibility::Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex((module.signatures.len() - 1) as u16), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }); - } -} - -fn leaf_module(name: &str) -> CompiledModule { - let mut module = empty_module(); - module.identifiers[0] = Identifier::new(name).unwrap(); - module.address_identifiers[0] = AccountAddress::ONE; - module -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/locals.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/locals.rs deleted file mode 100644 index 4789fd9131d..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/locals.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex, - IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, - Visibility::Public, -}; -use move_bytecode_verifier_meter::bound::BoundMeter; -use move_core_types::{identifier::Identifier, vm_status::StatusCode}; - -use crate::unit_tests::production_config; - -#[test] -fn test_locals() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-jjqw-f9pc-525j - let mut m = empty_module(); - - const MAX_BASIC_BLOCKS: u16 = 1024; - const MAX_LOCALS: u8 = 255; - const NUM_FUNCTIONS: u16 = 16; - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: true, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // signature of locals in f1..f - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::U8) - .take(MAX_LOCALS as usize) - .collect(), - )); - - m.identifiers.push(Identifier::new("pwn").unwrap()); - - // create returns_bool_and_u64 - m.signatures - .push(Signature(vec![SignatureToken::Bool, SignatureToken::U8])); - m.identifiers - .push(Identifier::new("returns_bool_and_u64").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - parameters: SignatureIndex(0), - return_: SignatureIndex(2), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::LdTrue, Bytecode::LdU8(0), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // create other functions - for i in 1..(NUM_FUNCTIONS + 1) { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 1), // the +1 accounts for returns_bool_and_u64 - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i + 1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(1), - code: vec![], - jump_tables: vec![], - }), - }); - - let code = &mut m.function_defs[i as usize + 1].code.as_mut().unwrap().code; - - for _ in 0..(MAX_BASIC_BLOCKS / 2 - MAX_LOCALS as u16 - 3) { - code.push(Bytecode::LdTrue); - code.push(Bytecode::BrTrue((code.len() + 2) as u16)); - code.push(Bytecode::Ret); - code.push(Bytecode::LdTrue); - code.push(Bytecode::BrTrue(0)); - } - for i in 0..MAX_LOCALS { - code.push(Bytecode::Call(FunctionHandleIndex(1))); // calls returns_bool_and_u64 - code.push(Bytecode::StLoc(i)); // i'th local is now available for the first time - code.push(Bytecode::BrTrue(0)); - } - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_locals", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/loop_summary_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/loop_summary_tests.rs deleted file mode 100644 index bae3df1a339..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/loop_summary_tests.rs +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_abstract_interpreter::control_flow_graph::VMControlFlowGraph; -use move_binary_format::file_format::Bytecode; -use move_bytecode_verifier::loop_summary::{LoopPartition, LoopSummary}; - -macro_rules! assert_node { - ( $summary:ident, $node:expr ; $block:expr, $preds:expr, $descs:expr, $backs:expr ) => { - let (s, n) = (&$summary, $node); - assert_eq!(s.block(n), $block, "Block"); - - let descs = $descs; - for d in descs { - assert!(s.is_descendant(n, *d), "{:?} -> {:?}", n, d) - } - - assert_eq!(s.pred_edges(n), $preds, "Predecessor Edges"); - assert_eq!(s.back_edges(n), $backs, "Back Edges"); - }; -} - -#[test] -fn linear_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - Nop, - // - Branch(2), - // B2, L1 - Nop, - // - Branch(4), - // B4, L2 - Ret, - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 3); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[1]; - /* block */ 2, - /* preds */ &[n[0]], - /* descs */ &[n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[2]; - /* block */ 4, - /* preds */ &[n[1]], - /* descs */ &[], - /* backs */ &[] - ); -} - -#[test] -fn non_loop_back_branch_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - Nop, - // - Branch(3), - // B2, L2 - Ret, - // B3, L1 - Branch(2), - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 3); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[1]; - /* block */ 3, - /* preds */ &[n[0]], - /* descs */ &[n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[2]; - /* block */ 2, - /* preds */ &[n[1]], - /* descs */ &[], - /* backs */ &[] - ); -} - -#[test] -fn branching_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - LdTrue, - // - BrTrue(3), - // B2, L2 - Nop, - // B3, L1 - Ret, - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 3); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[1]; - /* block */ 3, - /* preds */ &[n[0], n[2]], - /* descs */ &[], - /* backs */ &[] - ); - - assert_node!( - summary, n[2]; - /* block */ 2, - /* preds */ &[n[0]], - /* descs */ &[], - /* backs */ &[] - ); - - // Although L2 -> L1 is an edge in the CFG, it's not an edge in the DFST, so L2 - // is said to have no descendants. - assert!(!summary.is_descendant(n[2], n[1])); -} - -#[test] -fn looping_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - LdTrue, - // - BrTrue(4), - // B2, L2 - Nop, - // - Branch(0), - // B4, L1 - Ret, - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 3); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2]], - /* backs */ &[n[2]] - ); - - assert_node!( - summary, n[1]; - /* block */ 4, - /* preds */ &[n[0]], - /* descs */ &[], - /* backs */ &[] - ); - - assert_node!( - summary, n[2]; - /* block */ 2, - /* preds */ &[n[0]], - /* descs */ &[], - /* backs */ &[] - ); -} - -#[test] -fn branches_in_loops_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - LdTrue, - // - BrTrue(3), - // B2, L3 - Nop, - // B3, L1 - LdFalse, - // - BrFalse(0), - // B5, L2 - Ret, - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 4); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2], n[3]], - /* backs */ &[n[1]] - ); - - assert_node!( - summary, n[1]; - /* block */ 3, - /* preds */ &[n[0], n[3]], - /* descs */ &[n[2]], - /* backs */ &[] - ); - - assert_node!( - summary, n[2]; - /* block */ 5, - /* preds */ &[n[1]], - /* descs */ &[], - /* backs */ &[] - ); - - assert_node!( - summary, n[3]; - /* block */ 2, - /* preds */ &[n[0]], - /* descs */ &[], - /* backs */ &[] - ); -} - -#[test] -fn loops_in_branches_summary() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - LdTrue, - // - BrTrue(8), - // B2, L5 - Nop, - // B3, L6 - LdFalse, - // - BrFalse(3), - // B5, L7 - LdTrue, - // - BrTrue(2), - // B7, L8 - Branch(13), - // B8, L1 - Nop, - // B9, L2 - LdTrue, - // - BrTrue(8), - // B11, L3 - LdFalse, - // - BrFalse(9), - // B13, L4 - Ret, - ], - &[], - )) - }; - - let n: Vec<_> = summary.preorder().collect(); - - assert_eq!(n.len(), 9); - - assert_node!( - summary, n[0]; - /* block */ 0, - /* preds */ &[], - /* descs */ &[n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8]], - /* backs */ &[] - ); - - assert_node!( - summary, n[1]; - /* block */ 8, - /* preds */ &[n[0]], - /* descs */ &[n[2], n[3], n[4]], - /* backs */ &[n[2]] - ); - - assert_node!( - summary, n[2]; - /* block */ 9, - /* preds */ &[n[1]], - /* descs */ &[n[3], n[4]], - /* backs */ &[n[3]] - ); - - assert_node!( - summary, n[3]; - /* block */ 11, - /* preds */ &[n[2]], - /* descs */ &[n[4]], - /* backs */ &[] - ); - - assert_node!( - summary, n[4]; - /* block */ 13, - /* preds */ &[n[3], n[8]], - /* descs */ &[], - /* backs */ &[] - ); - - assert_node!( - summary, n[5]; - /* block */ 2, - /* preds */ &[n[0]], - /* descs */ &[n[6], n[7], n[8]], - /* backs */ &[n[7]] - ); - - assert_node!( - summary, n[6]; - /* block */ 3, - /* preds */ &[n[5]], - /* descs */ &[n[7], n[8]], - /* backs */ &[n[6]] - ); - - assert_node!( - summary, n[7]; - /* block */ 5, - /* preds */ &[n[6]], - /* descs */ &[n[8]], - /* backs */ &[] - ); - - assert_node!( - summary, n[8]; - /* block */ 7, - /* preds */ &[n[7]], - /* descs */ &[], - /* backs */ &[] - ); -} - -#[test] -fn loop_collapsing() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - LdTrue, - // - BrTrue(4), - // B2, L2 - Nop, - // - Branch(0), - // B4, L1 - Ret, - ], - &[], - )) - }; - - let mut partition = LoopPartition::new(&summary); - let n: Vec<_> = summary.preorder().collect(); - - for id in &n { - assert_eq!(*id, partition.containing_loop(*id), "Self-parent {:?}", id); - } - - assert_eq!(partition.collapse_loop(n[0], &[n[2]].into()), 1); - assert_eq!(partition.containing_loop(n[0]), n[0]); - assert_eq!(partition.containing_loop(n[1]), n[1]); - assert_eq!(partition.containing_loop(n[2]), n[0]); -} - -#[test] -fn nested_loop_collapsing() { - let summary = { - use Bytecode::*; - LoopSummary::new(&VMControlFlowGraph::new( - &[ - // B0, L0 - Nop, - // B1, L1 - LdTrue, - // - BrTrue(1), - // B3, L2 - LdFalse, - // - BrFalse(0), - // B5, L3 - LdTrue, - // - BrTrue(0), - // B7, L4 - Ret, - ], - &[], - )) - }; - - let mut partition = LoopPartition::new(&summary); - let n: Vec<_> = summary.preorder().collect(); - - // Self-loop is a special case -- its depth should still be bumped. - assert_eq!(partition.collapse_loop(n[1], &[].into()), 1); - assert_eq!(partition.collapse_loop(n[0], &[n[1], n[2]].into()), 2); - assert_eq!(partition.collapse_loop(n[0], &[n[0], n[3]].into()), 3); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/many_back_edges.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/many_back_edges.rs deleted file mode 100644 index b8b488cbafb..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/many_back_edges.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex, - IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, - Visibility::Public, -}; -use move_bytecode_verifier_meter::bound::BoundMeter; -use move_core_types::{identifier::Identifier, vm_status::StatusCode}; - -use crate::unit_tests::production_config; - -const MAX_BASIC_BLOCKS: u16 = 1024; -const MAX_LOCALS: u8 = 255; - -const NUM_FUNCTIONS: u16 = 16; - -#[test] -fn many_backedges() { - let mut m = empty_module(); - - // signature of locals in f1..f - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::U8) - .take(MAX_LOCALS as usize) - .collect(), - )); - - // create returns_bool_and_u64 - m.signatures - .push(Signature(vec![SignatureToken::Bool, SignatureToken::U8])); - m.identifiers - .push(Identifier::new("returns_bool_and_u64").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - parameters: SignatureIndex(0), - return_: SignatureIndex(2), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::LdTrue, Bytecode::LdU8(0), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // create other functions - for i in 1..(NUM_FUNCTIONS + 1) { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 1), // the +1 accounts for returns_bool_and_u64 - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(1), - code: vec![], - jump_tables: vec![], - }), - }); - - let code = &mut m.function_defs[i as usize].code.as_mut().unwrap().code; - - for _ in 0..(MAX_BASIC_BLOCKS - MAX_LOCALS as u16 - 2) { - code.push(Bytecode::LdTrue); - code.push(Bytecode::BrTrue(0)); - } - for i in 0..MAX_LOCALS { - code.push(Bytecode::Call(FunctionHandleIndex(0))); // calls returns_bool_and_u64 - code.push(Bytecode::StLoc(i)); // i'th local is now available for the first time - code.push(Bytecode::BrTrue(0)); - } - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "many_backedges", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs deleted file mode 100644 index 4ed2fb574f7..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format_common::VERSION_MAX; -use move_vm_config::verifier::{ - MeterConfig, VerifierConfig, DEFAULT_MAX_CONSTANT_VECTOR_LEN, DEFAULT_MAX_IDENTIFIER_LENGTH, - DEFAULT_MAX_VARIANTS, -}; - -pub mod binary_samples; -pub mod bounds_tests; -pub mod code_unit_tests; -pub mod constants_tests; -pub mod control_flow_tests; -pub mod duplication_tests; -pub mod generic_ops_tests; -pub mod large_type_test; -pub mod limit_tests; -pub mod locals; -pub mod loop_summary_tests; -pub mod many_back_edges; -pub mod negative_stack_size_tests; -pub mod reference_safety_tests; -pub mod signature_tests; -pub mod vec_pack_tests; - -/// Configuration used in production. -pub(crate) fn production_config() -> (VerifierConfig, MeterConfig) { - ( - VerifierConfig { - max_loop_depth: Some(5), - max_generic_instantiation_length: Some(32), - max_function_parameters: Some(128), - max_basic_blocks: Some(1024), - max_basic_blocks_in_script: Some(1024), - max_value_stack_size: 1024, - max_type_nodes: Some(256), - max_push_size: Some(10000), - max_dependency_depth: Some(100), - max_data_definitions: Some(200), - max_fields_in_struct: Some(30), - max_function_definitions: Some(1000), - - // Do not use back edge constraints as they are superseded by metering - max_back_edges_per_function: None, - max_back_edges_per_module: None, - - max_constant_vector_len: Some(DEFAULT_MAX_CONSTANT_VECTOR_LEN), - max_identifier_len: Some(DEFAULT_MAX_IDENTIFIER_LENGTH), - bytecode_version: VERSION_MAX, - max_variants_in_enum: Some(DEFAULT_MAX_VARIANTS), - }, - MeterConfig::old_default(), - ) -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs deleted file mode 100644 index 56844c517a1..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::Bytecode; -use move_bytecode_verifier::{ability_cache::AbilityCache, code_unit_verifier}; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::vm_status::StatusCode; - -use crate::support::dummy_procedure_module; - -#[test] -fn one_pop_no_push() { - let module = &dummy_procedure_module(vec![Bytecode::Pop, Bytecode::Ret]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK - ); -} - -#[test] -fn one_pop_one_push() { - // Height: 0 + (-1 + 1) = 0 would have passed original usage verifier - let module = &dummy_procedure_module(vec![Bytecode::ReadRef, Bytecode::Ret]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK - ); -} - -#[test] -fn two_pop_one_push() { - // Height: 0 + 1 + (-2 + 1) = 0 would have passed original usage verifier - let module = &dummy_procedure_module(vec![Bytecode::LdU64(0), Bytecode::Add, Bytecode::Ret]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK - ); -} - -#[test] -fn two_pop_no_push() { - let module = &dummy_procedure_module(vec![Bytecode::WriteRef, Bytecode::Ret]); - let ability_cache = &mut AbilityCache::new(module); - let result = code_unit_verifier::verify_module( - &Default::default(), - module, - ability_cache, - &mut DummyMeter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/reference_safety_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/reference_safety_tests.rs deleted file mode 100644 index 8be4f8a621b..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/reference_safety_tests.rs +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex, - IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, - Visibility::Public, -}; -use move_bytecode_verifier_meter::bound::BoundMeter; -use move_core_types::{identifier::Identifier, vm_status::StatusCode}; - -use crate::unit_tests::production_config; - -#[test] -fn test_bicliques() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-xm6p-ffcq-5p2v - const NUM_LOCALS: u8 = 128; - const NUM_CALLS: u16 = 76; - const NUM_FUNCTIONS: u16 = 1; - - let mut m = empty_module(); - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(0)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // create take_and_return_references - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::Reference(Box::new(SignatureToken::U64))) - .take(NUM_LOCALS as usize) - .collect(), - )); - m.identifiers - .push(Identifier::new("take_and_return_references").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - parameters: SignatureIndex(1), - return_: SignatureIndex(1), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![], - jump_tables: vec![], - }), - }); - let code = &mut m.function_defs[1].code.as_mut().unwrap().code; - for i in 0..NUM_LOCALS { - code.push(Bytecode::MoveLoc(i)); - } - code.push(Bytecode::Ret); - - // create swallow_references - m.identifiers - .push(Identifier::new("swallow_references").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(2), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(2), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Ret], - jump_tables: vec![], - }), - }); - - // create other functions - for i in 1..(NUM_FUNCTIONS + 1) { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 2), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i + 2), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![], - jump_tables: vec![], - }), - }); - let code = &mut m.function_defs[i as usize + 2].code.as_mut().unwrap().code; - for j in 0..NUM_LOCALS { - code.push(Bytecode::CopyLoc(j)); - } - for _ in 0..NUM_CALLS { - code.push(Bytecode::Call(FunctionHandleIndex(1))); - } - code.push(Bytecode::Call(FunctionHandleIndex(2))); - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_bicliques", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} - -#[test] -fn test_merge_state_large_graph() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-g8v8-fw4c-8h82 - const N: u8 = 127; - const NUM_NOP_BLOCKS: u16 = 950; - const NUM_FUNCTIONS: u16 = 18; - - let mut m = empty_module(); - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(0)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::Reference(Box::new(SignatureToken::U8))) - .take(N as usize) - .collect(), - )); - - m.identifiers.push(Identifier::new("return_refs").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - parameters: SignatureIndex(0), - return_: SignatureIndex(1), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(1)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - m.identifiers - .push(Identifier::new("take_and_return_refs").unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(2), - parameters: SignatureIndex(1), - return_: SignatureIndex(1), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(2), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(1)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - for i in 0..NUM_FUNCTIONS { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 3), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i + 3), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(1), - code: vec![], - jump_tables: vec![], - }), - }); - let code = &mut m.function_defs[i as usize + 3].code.as_mut().unwrap().code; - for j in 0..N { - code.push(Bytecode::CopyLoc(j)); - } - code.push(Bytecode::Call(FunctionHandleIndex(2))); - for j in 0..N { - code.push(Bytecode::StLoc(N + j)); - } - for _ in 0..NUM_NOP_BLOCKS { - code.push(Bytecode::LdTrue); - code.push(Bytecode::BrTrue(0)); - } - - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_merge_state_large_graph", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} - -#[test] -fn test_merge_state() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-g8v8-fw4c-8h82 - const NUM_NOP_BLOCKS: u16 = 965; - const NUM_LOCALS: u8 = 32; - const NUM_FUNCTIONS: u16 = 21; - - let mut m = empty_module(); - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Bytecode::Call(FunctionHandleIndex(0)), Bytecode::Ret], - jump_tables: vec![], - }), - }); - - m.signatures - .push(Signature(vec![SignatureToken::Reference(Box::new( - SignatureToken::U8, - ))])); - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::Reference(Box::new(SignatureToken::U8))) - .take(NUM_LOCALS as usize - 1) - .collect(), - )); - - for i in 0..NUM_FUNCTIONS { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i + 1), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i + 1), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(2), - code: vec![], - jump_tables: vec![], - }), - }); - let code = &mut m.function_defs[i as usize + 1].code.as_mut().unwrap().code; - // create reference id - code.push(Bytecode::CopyLoc(0)); - code.push(Bytecode::StLoc(1)); - // create a path of length NUM_LOCALS - 1 in the borrow graph - for j in 0..(NUM_LOCALS - 2) { - // create Ref(new_id) and factor in empty-path edge id -> new_id - code.push(Bytecode::CopyLoc(1)); - // can't leave those references on stack since basic blocks need to be - // stack-neutral - code.push(Bytecode::StLoc(j + 2)); - } - for _ in 0..NUM_NOP_BLOCKS { - code.push(Bytecode::LdTrue); - // create back edge to first block - code.push(Bytecode::BrTrue(0)); - } - - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_merge_state", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} - -#[test] -fn test_copyloc_pop() { - // See also: github.com/aptos-labs/aptos-core/security/advisories/ - // GHSA-2qvr-c9qp-wch7 - const NUM_COPYLOCS: u16 = 1880; - const NUM_CHILDREN: u16 = 1020; - const NUM_FUNCTIONS: u16 = 2; - - let mut m = empty_module(); - - // parameters of f0, f1, ... - m.signatures - .push(Signature(vec![SignatureToken::Reference(Box::new( - SignatureToken::Vector(Box::new(SignatureToken::U8)), - ))])); - // locals of f0, f1, ... - m.signatures.push(Signature(vec![ - SignatureToken::Reference(Box::new(SignatureToken::Vector(Box::new( - SignatureToken::U8, - )))), - SignatureToken::U8, /* ignore this, it's just here because I don't want to fix indices - * and the TypeParameter after removing the collision */ - ])); - // for VecImmBorrow - m.signatures.push(Signature( - std::iter::repeat(SignatureToken::U8).take(1).collect(), - )); - m.signatures - .push(Signature(vec![SignatureToken::TypeParameter(0)])); - - for i in 0..NUM_FUNCTIONS { - m.identifiers - .push(Identifier::new(format!("f{}", i)).unwrap()); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(i), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(i), - visibility: Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(2), - code: vec![], - jump_tables: vec![], - }), - }); - let code = &mut m.function_defs[i as usize].code.as_mut().unwrap().code; - - // create reference id - code.push(Bytecode::CopyLoc(0)); - code.push(Bytecode::StLoc(1)); - // create NUM_CHLIDREN children of id - for _ in 0..NUM_CHILDREN { - code.push(Bytecode::CopyLoc(1)); - code.push(Bytecode::LdU64(0)); - code.push(Bytecode::VecImmBorrow(SignatureIndex(3))); - } - // then do a whole lot of copylocs on that reference - for _ in 0..NUM_COPYLOCS { - code.push(Bytecode::CopyLoc(1)); - code.push(Bytecode::Pop); - } - for _ in 0..NUM_CHILDREN { - code.push(Bytecode::Pop); - } - - code.push(Bytecode::Ret); - } - - let (verifier_config, meter_config) = production_config(); - let mut meter = BoundMeter::new(meter_config); - let result = move_bytecode_verifier::verify_module_with_config_for_test( - "test_copyloc_pop", - &verifier_config, - &m, - &mut meter, - ); - assert_eq!( - result.unwrap_err().major_status(), - StatusCode::CONSTRAINT_NOT_SATISFIED - ); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs deleted file mode 100644 index 5e9ad8923fc..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - Bytecode::*, CompiledModule, SignatureToken::*, Visibility::Public, *, -}; -use move_bytecode_verifier::{ - ability_cache::AbilityCache, verify_module_unmetered, verify_module_with_config_for_test, - SignatureChecker, -}; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode, -}; - -use crate::unit_tests::production_config; - -#[test] -fn test_reference_of_reference() { - let mut m = basic_test_module(); - m.signatures[0] = Signature(vec![Reference(Box::new(Reference(Box::new( - SignatureToken::Bool, - ))))]); - let ability_cache = &mut AbilityCache::new(&m); - let errors = SignatureChecker::verify_module(&m, ability_cache, &mut DummyMeter); - assert!(errors.is_err()); -} - -#[test] -fn no_verify_locals_good() { - let compiled_module_good = CompiledModule { - version: move_binary_format::file_format_common::VERSION_MAX, - module_handles: vec![ModuleHandle { - address: AddressIdentifierIndex(0), - name: IdentifierIndex(0), - }], - self_module_handle_idx: ModuleHandleIndex(0), - datatype_handles: vec![], - signatures: vec![ - Signature(vec![Address]), - Signature(vec![U64]), - Signature(vec![]), - ], - function_handles: vec![ - FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - return_: SignatureIndex(2), - parameters: SignatureIndex(0), - type_parameters: vec![], - }, - FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(2), - return_: SignatureIndex(2), - parameters: SignatureIndex(1), - type_parameters: vec![], - }, - ], - field_handles: vec![], - friend_decls: vec![], - struct_def_instantiations: vec![], - function_instantiations: vec![], - field_instantiations: vec![], - identifiers: vec![ - Identifier::new("Bad").unwrap(), - Identifier::new("blah").unwrap(), - Identifier::new("foo").unwrap(), - ], - address_identifiers: vec![AccountAddress::new([0; AccountAddress::LENGTH])], - constant_pool: vec![], - metadata: vec![], - struct_defs: vec![], - function_defs: vec![ - FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Visibility::Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![Ret], - jump_tables: vec![], - }), - }, - FunctionDefinition { - function: FunctionHandleIndex(1), - visibility: Visibility::Public, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(1), - code: vec![Ret], - jump_tables: vec![], - }), - }, - ], - enum_defs: vec![], - enum_def_instantiations: vec![], - variant_handles: vec![], - variant_instantiation_handles: vec![], - }; - assert!(verify_module_unmetered(&compiled_module_good).is_ok()); -} - -#[test] -fn big_signature_test() { - const N_TYPE_PARAMS: usize = 5; - const INSTANTIATION_DEPTH: usize = 3; - const VECTOR_DEPTH: usize = 250; - let mut st = SignatureToken::U8; - for _ in 0..VECTOR_DEPTH { - st = SignatureToken::Vector(Box::new(st)); - } - for _ in 0..INSTANTIATION_DEPTH { - let type_params = vec![st; N_TYPE_PARAMS]; - st = SignatureToken::DatatypeInstantiation(Box::new((DatatypeHandleIndex(0), type_params))); - } - - const N_READPOP: u16 = 7500; - - let mut code = vec![]; - // 1. ImmBorrowLoc: ... ref - // 2. ReadRef: ... value - // 3. Pop: ... - for _ in 0..N_READPOP { - code.push(Bytecode::ImmBorrowLoc(0)); - code.push(Bytecode::ReadRef); - code.push(Bytecode::Pop); - } - code.push(Bytecode::Ret); - - let type_param_constraints = DatatypeTyParameter { - constraints: AbilitySet::EMPTY, - is_phantom: false, - }; - - let module = CompiledModule { - version: 5, - self_module_handle_idx: ModuleHandleIndex(0), - module_handles: vec![ModuleHandle { - address: AddressIdentifierIndex(0), - name: IdentifierIndex(0), - }], - datatype_handles: vec![DatatypeHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(1), - abilities: AbilitySet::ALL, - type_parameters: vec![type_param_constraints; N_TYPE_PARAMS], - }], - function_handles: vec![FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(0), - parameters: SignatureIndex(1), - return_: SignatureIndex(0), - type_parameters: vec![], - }], - field_handles: vec![], - friend_decls: vec![], - struct_def_instantiations: vec![], - function_instantiations: vec![], - field_instantiations: vec![], - signatures: vec![Signature(vec![]), Signature(vec![st])], - identifiers: vec![ - Identifier::new("f").unwrap(), - Identifier::new("generic_struct").unwrap(), - ], - address_identifiers: vec![AccountAddress::ONE], - constant_pool: vec![], - metadata: vec![], - struct_defs: vec![StructDefinition { - struct_handle: DatatypeHandleIndex(0), - field_information: StructFieldInformation::Native, - }], - function_defs: vec![FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Public, - is_entry: true, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code, - jump_tables: vec![], - }), - }], - enum_def_instantiations: vec![], - enum_defs: vec![], - variant_handles: vec![], - variant_instantiation_handles: vec![], - }; - - // save module and verify that it can ser/de - let mut mvbytes = vec![]; - module.serialize(&mut mvbytes).unwrap(); - let module = CompiledModule::deserialize_with_defaults(&mvbytes).unwrap(); - - let res = verify_module_with_config_for_test( - "big_signature_test", - &production_config().0, - &module, - &mut DummyMeter, - ) - .unwrap_err(); - assert_eq!(res.major_status(), StatusCode::TOO_MANY_TYPE_NODES); -} diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/vec_pack_tests.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/vec_pack_tests.rs deleted file mode 100644 index 1f7c54d8991..00000000000 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/vec_pack_tests.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::file_format::{ - empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex, - IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, Visibility, -}; -use move_bytecode_verifier_meter::dummy::DummyMeter; -use move_core_types::{identifier::Identifier, vm_status::StatusCode}; - -use crate::unit_tests::production_config; - -fn vec_sig(len: usize) -> SignatureToken { - if len > 0 { - SignatureToken::Vector(Box::new(vec_sig(len - 1))) - } else { - SignatureToken::U8 - } -} - -#[test] -fn test_vec_pack() { - let mut m = empty_module(); - - let sig = SignatureIndex(m.signatures.len() as u16); - m.signatures.push(Signature(vec![vec_sig(255)])); - - m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex(0), - visibility: Visibility::Private, - is_entry: false, - acquires_global_resources: vec![], - code: Some(CodeUnit { - locals: SignatureIndex(0), - code: vec![], - jump_tables: vec![], - }), - }); - - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex(0), - name: IdentifierIndex(m.identifiers.len() as u16), - parameters: SignatureIndex(0), - return_: SignatureIndex(0), - type_parameters: vec![], - }); - m.identifiers - .push(Identifier::new("foo".to_string()).unwrap()); - - const COUNT: usize = 3000; - - m.function_defs[0].code.as_mut().unwrap().code = - std::iter::once(&[Bytecode::VecPack(sig, 0)][..]) - .chain( - std::iter::repeat( - &[Bytecode::VecUnpack(sig, 1024), Bytecode::VecPack(sig, 1024)][..], - ) - .take(COUNT), - ) - .chain(std::iter::once(&[Bytecode::Pop, Bytecode::Ret][..])) - .flatten() - .cloned() - .collect(); - - let res = move_bytecode_verifier::verify_module_with_config_for_test( - "test_vec_pack", - &production_config().0, - &m, - &mut DummyMeter, - ) - .unwrap_err(); - assert_eq!(res.major_status(), StatusCode::VALUE_STACK_PUSH_OVERFLOW); -} diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/Cargo.toml b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/Cargo.toml deleted file mode 100644 index b0dd9fc240b..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "move-abstract-interpreter-v0" -version = "0.1.0" -authors = ["The Move Contributors"] -edition = "2021" -license = "Apache-2.0" -publish = false -description = "Move abstract interpreter" - -[dependencies] -move-binary-format.workspace = true -move-bytecode-verifier-meter.workspace = true - -[dev-dependencies] -itertools.workspace = true - -[features] -default = [] diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/absint.rs b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/absint.rs deleted file mode 100644 index 480ff082c60..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/absint.rs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::BTreeMap; - -use move_binary_format::{ - errors::PartialVMResult, - file_format::{ - AbilitySet, Bytecode, CodeOffset, CodeUnit, FunctionDefinitionIndex, FunctionHandle, - Signature, - }, - CompiledModule, -}; -use move_bytecode_verifier_meter::{Meter, Scope}; - -use crate::control_flow_graph::{BlockId, ControlFlowGraph, VMControlFlowGraph}; - -/// A `FunctionContext` holds all the information needed by the verifier for -/// `FunctionDefinition`.` A control flow graph is built for a function when the -/// `FunctionContext` is created. -pub struct FunctionContext<'a> { - index: Option, - code: &'a CodeUnit, - parameters: &'a Signature, - return_: &'a Signature, - locals: &'a Signature, - type_parameters: &'a [AbilitySet], - cfg: VMControlFlowGraph, -} - -/// Trait for finite-height abstract domains. Infinite height domains would -/// require a more complex trait with widening and a partial order. -pub trait AbstractDomain: Clone + Sized { - fn join( - &mut self, - other: &Self, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult; -} - -#[derive(Debug)] -pub enum JoinResult { - Changed, - Unchanged, -} - -#[allow(dead_code)] -#[derive(Clone)] -pub struct BlockInvariant { - /// Precondition of the block - pre: State, -} - -/// A map from block id's to the pre/post of each block after a fixed point is -/// reached. -#[allow(dead_code)] -pub type InvariantMap = BTreeMap>; - -/// Costs for metered verification -const ANALYZE_FUNCTION_BASE_COST: u128 = 10; -const EXECUTE_BLOCK_BASE_COST: u128 = 10; -const PER_BACKEDGE_COST: u128 = 10; -const PER_SUCCESSOR_COST: u128 = 10; - -/// Take a pre-state + instruction and mutate it to produce a post-state -/// Auxiliary data can be stored in self. -pub trait TransferFunctions { - type State: AbstractDomain; - type Error; - - /// Execute local@instr found at index local@index in the current basic - /// block from pre-state local@pre. - /// Should return an AnalysisError if executing the instruction is - /// unsuccessful, and () if the effects of successfully executing - /// local@instr have been reflected by mutatating local@pre. - /// Auxiliary data from the analysis that is not part of the abstract state - /// can be collected by mutating local@self. - /// The last instruction index in the current block is local@last_index. - /// Knowing this information allows clients to detect the end of a basic - /// block and special-case appropriately (e.g., normalizing the abstract - /// state before a join). - fn execute( - &mut self, - pre: &mut Self::State, - instr: &Bytecode, - index: CodeOffset, - last_index: CodeOffset, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult<()>; -} - -pub trait AbstractInterpreter: TransferFunctions { - /// Analyze procedure local@function_context starting from pre-state - /// local@initial_state. - fn analyze_function( - &mut self, - initial_state: Self::State, - function_context: &FunctionContext, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult<()> { - meter.add(Scope::Function, ANALYZE_FUNCTION_BASE_COST)?; - let mut inv_map = InvariantMap::new(); - let entry_block_id = function_context.cfg().entry_block_id(); - let mut next_block = Some(entry_block_id); - inv_map.insert(entry_block_id, BlockInvariant { pre: initial_state }); - - while let Some(block_id) = next_block { - let block_invariant = match inv_map.get_mut(&block_id) { - Some(invariant) => invariant, - None => { - // This can only happen when all predecessors have errors, - // so skip the block and move on to the next one - next_block = function_context.cfg().next_block(block_id); - continue; - } - }; - - let pre_state = &block_invariant.pre; - // Note: this will stop analysis after the first error occurs, to avoid the risk - // of subsequent crashes - let post_state = self.execute_block(block_id, pre_state, function_context, meter)?; - - let mut next_block_candidate = function_context.cfg().next_block(block_id); - // propagate postcondition of this block to successor blocks - for successor_block_id in function_context.cfg().successors(block_id) { - meter.add(Scope::Function, PER_SUCCESSOR_COST)?; - match inv_map.get_mut(successor_block_id) { - Some(next_block_invariant) => { - let join_result = { - let old_pre = &mut next_block_invariant.pre; - old_pre.join(&post_state, meter) - }?; - match join_result { - JoinResult::Unchanged => { - // Pre is the same after join. Reanalyzing this - // block would produce - // the same post - } - JoinResult::Changed => { - // If the cur->successor is a back edge, jump back to the beginning - // of the loop, instead of the normal next block - if function_context - .cfg() - .is_back_edge(block_id, *successor_block_id) - { - meter.add(Scope::Function, PER_BACKEDGE_COST)?; - next_block_candidate = Some(*successor_block_id); - break; - } - } - } - } - None => { - // Haven't visited the next block yet. Use the post of the current block as - // its pre - inv_map.insert( - *successor_block_id, - BlockInvariant { - pre: post_state.clone(), - }, - ); - } - } - } - next_block = next_block_candidate; - } - Ok(()) - } - - fn execute_block( - &mut self, - block_id: BlockId, - pre_state: &Self::State, - function_context: &FunctionContext, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult { - meter.add(Scope::Function, EXECUTE_BLOCK_BASE_COST)?; - let mut state_acc = pre_state.clone(); - let block_end = function_context.cfg().block_end(block_id); - for offset in function_context.cfg().instr_indexes(block_id) { - let instr = &function_context.code().code[offset as usize]; - self.execute(&mut state_acc, instr, offset, block_end, meter)? - } - Ok(state_acc) - } -} - -impl<'a> FunctionContext<'a> { - // Creates a `FunctionContext` for a module function. - pub fn new( - module: &'a CompiledModule, - index: FunctionDefinitionIndex, - code: &'a CodeUnit, - function_handle: &'a FunctionHandle, - ) -> Self { - Self { - index: Some(index), - code, - parameters: module.signature_at(function_handle.parameters), - return_: module.signature_at(function_handle.return_), - locals: module.signature_at(code.locals), - type_parameters: &function_handle.type_parameters, - cfg: VMControlFlowGraph::new(&code.code, &code.jump_tables), - } - } - - pub fn index(&self) -> Option { - self.index - } - - pub fn code(&self) -> &'a CodeUnit { - self.code - } - - pub fn parameters(&self) -> &'a Signature { - self.parameters - } - - pub fn return_(&self) -> &'a Signature { - self.return_ - } - - pub fn locals(&self) -> &'a Signature { - self.locals - } - - pub fn type_parameters(&self) -> &'a [AbilitySet] { - self.type_parameters - } - - pub fn cfg(&self) -> &VMControlFlowGraph { - &self.cfg - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/control_flow_graph.rs b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/control_flow_graph.rs deleted file mode 100644 index 1ee2a267829..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/control_flow_graph.rs +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module defines the control-flow graph uses for bytecode verification. -use move_binary_format::file_format::{Bytecode, CodeOffset, VariantJumpTable}; -use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; - -// BTree/Hash agnostic type wrappers -type Map = BTreeMap; -type Set = BTreeSet; - -pub type BlockId = CodeOffset; - -/// A trait that specifies the basic requirements for a CFG -pub trait ControlFlowGraph { - /// Start index of the block ID in the bytecode vector - fn block_start(&self, block_id: BlockId) -> CodeOffset; - - /// End index of the block ID in the bytecode vector - fn block_end(&self, block_id: BlockId) -> CodeOffset; - - /// Successors of the block ID in the bytecode vector - fn successors(&self, block_id: BlockId) -> &Vec; - - /// Return the next block in traversal order - fn next_block(&self, block_id: BlockId) -> Option; - - /// Iterator over the indexes of instructions in this block - fn instr_indexes(&self, block_id: BlockId) -> Box>; - - /// Return an iterator over the blocks of the CFG - fn blocks(&self) -> Vec; - - /// Return the number of blocks (vertices) in the control flow graph - fn num_blocks(&self) -> u16; - - /// Return the id of the entry block for this control-flow graph - /// Note: even a CFG with no instructions has an (empty) entry block. - fn entry_block_id(&self) -> BlockId; - - /// Checks if the block ID is a loop head - fn is_loop_head(&self, block_id: BlockId) -> bool; - - /// Checks if the edge from cur->next is a back edge - /// returns false if the edge is not in the cfg - fn is_back_edge(&self, cur: BlockId, next: BlockId) -> bool; - - /// Return the number of back edges in the cfg - fn num_back_edges(&self) -> usize; -} - -struct BasicBlock { - exit: CodeOffset, - successors: Vec, -} - -/// The control flow graph that we build from the bytecode. -pub struct VMControlFlowGraph { - /// The basic blocks - blocks: Map, - /// Basic block ordering for traversal - traversal_successors: Map, - /// Map of loop heads with all of their back edges - loop_heads: Map>, -} - -impl BasicBlock { - pub fn display(&self, entry: BlockId) { - println!("+=======================+"); - println!("| Enter: {} |", entry); - println!("+-----------------------+"); - println!("==> Children: {:?}", self.successors); - println!("+-----------------------+"); - println!("| Exit: {} |", self.exit); - println!("+=======================+"); - } -} - -const ENTRY_BLOCK_ID: BlockId = 0; - -impl VMControlFlowGraph { - pub fn new(code: &[Bytecode], jump_tables: &[VariantJumpTable]) -> Self { - let code_len = code.len() as CodeOffset; - // First go through and collect block ids, i.e., offsets that begin basic - // blocks. Need to do this first in order to handle backwards edges. - let mut block_ids = Set::new(); - block_ids.insert(ENTRY_BLOCK_ID); - for pc in 0..code.len() { - VMControlFlowGraph::record_block_ids( - pc as CodeOffset, - code, - jump_tables, - &mut block_ids, - ); - } - - // Create basic blocks - let mut blocks = Map::new(); - let mut entry = 0; - let mut exit_to_entry = Map::new(); - for pc in 0..code.len() { - let co_pc = pc as CodeOffset; - - // Create a basic block - if Self::is_end_of_block(co_pc, code, &block_ids) { - let exit = co_pc; - exit_to_entry.insert(exit, entry); - let successors = Bytecode::get_successors(co_pc, code, jump_tables); - let bb = BasicBlock { exit, successors }; - blocks.insert(entry, bb); - entry = co_pc + 1; - } - } - let blocks = blocks; - assert_eq!(entry, code_len); - - // # Loop analysis - // - // This section identifies loops in the control-flow graph, picks a back edge - // and loop head (the basic block the back edge returns to), and decides - // the order that blocks are traversed during abstract interpretation - // (reverse post-order). - // - // The implementation is based on the algorithm for finding widening points in - // Section 4.1, "Depth-first numbering" of Bourdoncle [1993], "Efficient - // chaotic iteration strategies with widenings." - // - // NB. The comments below refer to a block's sub-graph -- the reflexive - // transitive closure of its successor edges, modulo cycles. - - #[derive(Copy, Clone)] - enum Exploration { - InProgress, - Done, - } - - let mut exploration: Map = Map::new(); - let mut stack = vec![ENTRY_BLOCK_ID]; - - // For every loop in the CFG that is reachable from the entry block, there is an - // entry in `loop_heads` mapping to all the back edges pointing to it, - // and vice versa. - // - // Entry in `loop_heads` implies loop in the CFG is justified by the comments in - // the loop below. Loop in the CFG implies entry in `loop_heads` is - // justified by considering the point at which the first node in that - // loop, `F` is added to the `exploration` map: - // - // - By definition `F` is part of a loop, meaning there is a block `L` such - // that: - // - // F - ... -> L -> F - // - // - `F` will not transition to `Done` until all the nodes reachable from it - // (including `L`) have been visited. - // - Because `F` is the first node seen in the loop, all the other nodes in the - // loop (including `L`) will be visited while `F` is `InProgress`. - // - Therefore, we will process the `L -> F` edge while `F` is `InProgress`. - // - Therefore, we will record a back edge to it. - let mut loop_heads: Map> = Map::new(); - - // Blocks appear in `post_order` after all the blocks in their (non-reflexive) - // sub-graph. - let mut post_order = Vec::with_capacity(blocks.len()); - - while let Some(block) = stack.pop() { - match exploration.entry(block) { - Entry::Vacant(entry) => { - // Record the fact that exploration of this block and its sub-graph has started. - entry.insert(Exploration::InProgress); - - // Push the block back on the stack to finish processing it, and mark it as done - // once its sub-graph has been traversed. - stack.push(block); - - for succ in &blocks[&block].successors { - match exploration.get(succ) { - // This successor has never been visited before, add it to the stack to - // be explored before `block` gets marked `Done`. - None => stack.push(*succ), - - // This block's sub-graph was being explored, meaning it is a (reflexive - // transitive) predecessor of `block` as well as being a successor, - // implying a loop has been detected -- greedily choose the successor - // block as the loop head. - Some(Exploration::InProgress) => { - loop_heads.entry(*succ).or_default().insert(block); - } - - // Cross-edge detected, this block and its entire sub-graph (modulo - // cycles) has already been explored via a different path, and is - // already present in `post_order`. - Some(Exploration::Done) => { /* skip */ } - }; - } - } - - Entry::Occupied(mut entry) => match entry.get() { - // Already traversed the sub-graph reachable from this block, so skip it. - Exploration::Done => continue, - - // Finish up the traversal by adding this block to the post-order traversal - // after its sub-graph (modulo cycles). - Exploration::InProgress => { - post_order.push(block); - entry.insert(Exploration::Done); - } - }, - } - } - - let traversal_order = { - // This reverse post order is akin to a topological sort (ignoring cycles) and - // is different from a pre-order in the presence of diamond patterns - // in the graph. - post_order.reverse(); - post_order - }; - - // build a mapping from a block id to the next block id in the traversal order - let traversal_successors = traversal_order - .windows(2) - .map(|window| { - debug_assert!(window.len() == 2); - (window[0], window[1]) - }) - .collect(); - - VMControlFlowGraph { - blocks, - traversal_successors, - loop_heads, - } - } - - pub fn display(&self) { - for (entry, block) in &self.blocks { - block.display(*entry); - } - println!("Traversal: {:#?}", self.traversal_successors); - } - - fn is_end_of_block(pc: CodeOffset, code: &[Bytecode], block_ids: &Set) -> bool { - pc + 1 == (code.len() as CodeOffset) || block_ids.contains(&(pc + 1)) - } - - fn record_block_ids( - pc: CodeOffset, - code: &[Bytecode], - jump_tables: &[VariantJumpTable], - block_ids: &mut Set, - ) { - let bytecode = &code[pc as usize]; - - block_ids.extend(bytecode.offsets(jump_tables)); - - if bytecode.is_branch() && pc + 1 < (code.len() as CodeOffset) { - block_ids.insert(pc + 1); - } - } - - /// A utility function that implements BFS-reachability from block_id with - /// respect to get_targets function - fn traverse_by(&self, block_id: BlockId) -> Vec { - let mut ret = Vec::new(); - // We use this index to keep track of our frontier. - let mut index = 0; - // Guard against cycles - let mut seen = Set::new(); - - ret.push(block_id); - seen.insert(&block_id); - - while index < ret.len() { - let block_id = ret[index]; - index += 1; - let successors = self.successors(block_id); - for block_id in successors.iter() { - if !seen.contains(&block_id) { - ret.push(*block_id); - seen.insert(block_id); - } - } - } - - ret - } - - pub fn reachable_from(&self, block_id: BlockId) -> Vec { - self.traverse_by(block_id) - } -} - -impl ControlFlowGraph for VMControlFlowGraph { - // Note: in the following procedures, it's safe not to check bounds because: - // - Every CFG (even one with no instructions) has a block at ENTRY_BLOCK_ID - // - The only way to acquire new BlockId's is via block_successors() - // - block_successors only() returns valid BlockId's - // Note: it is still possible to get a BlockId from one CFG and use it in - // another CFG where it is not valid. The design does not attempt to prevent - // this abuse of the API. - - fn block_start(&self, block_id: BlockId) -> CodeOffset { - block_id - } - - fn block_end(&self, block_id: BlockId) -> CodeOffset { - self.blocks[&block_id].exit - } - - fn successors(&self, block_id: BlockId) -> &Vec { - &self.blocks[&block_id].successors - } - - fn next_block(&self, block_id: BlockId) -> Option { - debug_assert!(self.blocks.contains_key(&block_id)); - self.traversal_successors.get(&block_id).copied() - } - - fn instr_indexes(&self, block_id: BlockId) -> Box> { - Box::new(self.block_start(block_id)..=self.block_end(block_id)) - } - - fn blocks(&self) -> Vec { - self.blocks.keys().cloned().collect() - } - - fn num_blocks(&self) -> u16 { - self.blocks.len() as u16 - } - - fn entry_block_id(&self) -> BlockId { - ENTRY_BLOCK_ID - } - - fn is_loop_head(&self, block_id: BlockId) -> bool { - self.loop_heads.contains_key(&block_id) - } - - fn is_back_edge(&self, cur: BlockId, next: BlockId) -> bool { - self.loop_heads - .get(&next) - .map_or(false, |back_edges| back_edges.contains(&cur)) - } - - fn num_back_edges(&self) -> usize { - self.loop_heads - .iter() - .fold(0, |acc, (_, edges)| acc + edges.len()) - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/lib.rs b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/lib.rs deleted file mode 100644 index e103979117e..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod absint; -pub mod control_flow_graph; - -#[cfg(test)] -mod unit_tests; diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/control_flow_graph_tests.rs b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/control_flow_graph_tests.rs deleted file mode 100644 index 83cb0ca6fe3..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/control_flow_graph_tests.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use itertools::Itertools; - -use crate::control_flow_graph::{BlockId, ControlFlowGraph, VMControlFlowGraph}; - -use move_binary_format::file_format::{ - Bytecode, EnumDefinitionIndex, JumpTableInner, VariantJumpTable, VariantJumpTableIndex, -}; - -#[test] -fn traversal_no_loops() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0 */ LdTrue, - /* */ BrTrue(3), - /* L2 */ Branch(3), - /* L3 */ Ret, - ], - &[], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 3); - assert_eq!(traversal(&cfg), vec![0, 2, 3]); -} - -#[test] -fn traversal_no_loops_with_switch() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0 */ VariantSwitch(VariantJumpTableIndex::new(0)), - /* */ Nop, - /* */ Nop, - /* */ Nop, - /* */ Nop, - /* */ Nop, - /* */ BrTrue(8), - /* L2 */ Branch(8), - /* L3 */ Ret, - ], - &[VariantJumpTable { - // Doesn't matter - head_enum: EnumDefinitionIndex::new(0), - jump_table: JumpTableInner::Full(vec![1, 8, 2, 4]), - }], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 6); - assert_eq!(dbg!(traversal(&cfg)), vec![0, 1, 2, 4, 7, 8]); -} - -#[test] -fn traversal_loops() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0: Outer head */ LdTrue, - /* Outer break */ BrTrue(6), - /* L2: Inner head */ LdTrue, - /* Inner break */ BrTrue(5), - /* L4: Inner continue */ Branch(2), - /* Outer continue */ Branch(0), - /* L6: */ Ret, - ], - &[], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 5); - assert_eq!(traversal(&cfg), vec![0, 2, 4, 5, 6]); -} - -#[test] -fn traversal_loops_with_switch() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0: Outer head */ LdTrue, - /* Outer break */ BrTrue(4), - /* L2: Inner head */ VariantSwitch(VariantJumpTableIndex::new(0)), - /* Outer continue */ Branch(0), - /* L6: */ Ret, - ], - &[VariantJumpTable { - // Doesn't matter - head_enum: EnumDefinitionIndex::new(0), - jump_table: JumpTableInner::Full(vec![ - /* Inner break */ 3, /* Inner continue */ 2, - ]), - }], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 4); - assert_eq!(traversal(&cfg), vec![0, 2, 3, 4]); -} - -#[test] -fn traversal_non_loop_back_branch() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0 */ Branch(2), - /* L1 */ Ret, - /* L2 */ Branch(1), - ], - &[], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 3); - assert_eq!(traversal(&cfg), vec![0, 2, 1]); -} - -#[test] -fn traversal_non_loop_back_branch_variant_switch() { - let cfg = { - use Bytecode::*; - VMControlFlowGraph::new( - &[ - /* L0 */ VariantSwitch(VariantJumpTableIndex::new(0)), - /* L1 */ Ret, - /* L2 */ Branch(1), - ], - &[VariantJumpTable { - // Doesn't matter - head_enum: EnumDefinitionIndex::new(0), - jump_table: JumpTableInner::Full(vec![2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]), - }], - ) - }; - - cfg.display(); - assert_eq!(cfg.num_blocks(), 3); - assert_eq!(traversal(&cfg), vec![0, 2, 1]); -} - -#[test] -fn out_of_order_blocks_variant_switch() { - const PERMUTATION_BOUND: usize = 2000; - - let blocks = (0..=127) - .map(|i| { - ( - i, - vec![ - Bytecode::Pop, // Pop the value from the variant switch - Bytecode::LdU16(i), // Ld the number so we can track what block this is canonically - Bytecode::Pop, // Then pop it - Bytecode::Ret, // Then ret - ], - ) - }) - .collect::>(); - - let block_len = blocks.last().unwrap().1.len() as u16; - - let (canonical_blocks, canonical_traversal) = { - let jump_table = - JumpTableInner::Full(blocks.iter().map(|(i, _)| 1 + *i * block_len).collect()); - let mut start_block = vec![Bytecode::VariantSwitch(VariantJumpTableIndex::new(0))]; - start_block.extend(blocks.clone().into_iter().flat_map(|(_, block)| block)); - - let cfg = VMControlFlowGraph::new( - &start_block, - &[VariantJumpTable { - // Doesn't matter - head_enum: EnumDefinitionIndex::new(0), - jump_table, - }], - ); - - cfg.display(); - (cfg.num_blocks(), traversal(&cfg)) - }; - - assert_eq!(canonical_blocks, 129); - assert_eq!(canonical_traversal.len(), 129); - - for permutation in blocks.into_iter().permutations(128).take(PERMUTATION_BOUND) { - // orig index => new_index - // identity permutation == perm[i] == i; - let mut perm = vec![]; - let mut blocks = vec![Bytecode::VariantSwitch(VariantJumpTableIndex::new(0))]; - for (index, mut block) in permutation.into_iter() { - perm.push(index); - blocks.append(&mut block); - } - - let jump_table = JumpTableInner::Full(perm.iter().map(|i| 1 + *i * block_len).collect()); - - let cfg = VMControlFlowGraph::new( - &blocks, - &[VariantJumpTable { - // Doesn't matter - head_enum: EnumDefinitionIndex::new(0), - jump_table, - }], - ); - assert_eq!( - cfg.num_blocks(), - canonical_blocks, - "num blocks differ: Permutation: {:?}", - perm - ); - assert_eq!( - traversal(&cfg), - canonical_traversal, - "traversal differs: Permutation: {:?}", - perm - ); - } -} - -/// Return a vector containing the `BlockId`s from `cfg` in the order suggested by successively -/// calling `ControlFlowGraph::next_block` starting from the entry block. -fn traversal(cfg: &dyn ControlFlowGraph) -> Vec { - let mut order = Vec::with_capacity(cfg.num_blocks() as usize); - let mut next = Some(cfg.entry_block_id()); - - while let Some(block) = next { - order.push(block); - next = cfg.next_block(block); - } - - order -} diff --git a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/mod.rs b/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/mod.rs deleted file mode 100644 index 46e078986ad..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-abstract-interpreter/src/unit_tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod control_flow_graph_tests; diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/Cargo.toml b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/Cargo.toml deleted file mode 100644 index 66e4d921646..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "move-bytecode-verifier-v0" -version = "0.1.0" -authors = ["IOTA Foundation "] -edition = "2021" -license = "Apache-2.0" -publish = false -description = "Move bytecode verifier" - -[dependencies] -petgraph.workspace = true - -move-abstract-interpreter.workspace = true -move-abstract-stack.workspace = true -move-binary-format.workspace = true -move-borrow-graph.workspace = true -move-bytecode-verifier-meter.workspace = true -move-core-types.workspace = true -move-vm-config.workspace = true - -[features] -default = [] diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/README.md b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/README.md deleted file mode 100644 index f062b48a66f..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/README.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -id: bytecode-verifier -title: Bytecode Verifier -custom_edit_url: https://github.com/move-language/move/edit/main/language/move-bytecode-verifier/README.md ---- - -## Overview - -The bytecode verifier contains a static analysis tool for rejecting invalid Move bytecode. It checks the safety of stack usage, types, resources, and references. - -The body of each function in a compiled module is verified separately while trusting the correctness of function signatures in the module. Checking that each function signature matches its definition is a separate responsibility. The body of a function is a sequence of bytecode instructions. This instruction sequence is checked in several phases described below. - -## CFG Construction - -A control-flow graph is constructed by decomposing the instruction sequence into a collection of basic blocks. Each basic block contains a contiguous sequence of instructions; the set of all instructions is partitioned among the blocks. Each block ends with a branch or return instruction. The decomposition into blocks guarantees that branch targets land only at the beginning of some block. The decomposition also attempts to ensure that the generated blocks are maximal. However, the soundness of the analysis does not depend on maximality. - -## Stack Safety - -The execution of a block happens in the context of a stack and an array of local variables. The parameters of the function are a prefix of the array of local variables. Arguments and return values are passed across function calls via the stack. When a function starts executing, its arguments are already loaded into its parameters. Suppose the stack height is _n_ when a function starts executing; then valid bytecode must enforce the invariant that when execution lands at the beginning of a basic block, the stack height is _n_. Furthermore, at a return instruction, the stack height must be _n_+_k_ where _k_, s.t. _k_>=0 is the number of return values. The first phase of the analysis checks that this invariant is maintained by analyzing each block separately, calculating the effect of each instruction in the block on the stack height, checking that the height does not go below _n_, and that is left either at _n_ or _n_+_k_ (depending on the final instruction of the block and the return type of the function) at the end of the block. - -## Type Safety - -The second phase of the analysis checks that each operation, primitive or defined function, is invoked with arguments of appropriate types. The operands of an operation are values located either in a local variable or on the stack. The types of local variables of a function are already provided in the bytecode. However, the types of stack values are inferred. This inference and the type checking of each operation can be done separately for each block. Since the stack height at the beginning of each block is _n_ and does not go below _n_ during the execution of the block, we only need to model the suffix of the stack starting at _n_ for type checking the block instructions. We model this suffix using a stack of types on which types are pushed and popped as the instruction stream in a block is processed. Only the type stack and the statically-known types of local variables are needed to type check each instruction. - -## Resource Safety - -Resources represent the assets of the blockchain. As such, there are certain restrictions on these types that do not apply to normal values. Intuitively, resource values cannot be copied and must be used by the end of the transaction (this means that they are moved to global storage or destroyed). Concretely, the following restrictions apply: - -- `CopyLoc` and `StLoc` require that the type of local is not of resource kind. -- `WriteRef`, `Eq`, and `Neq` require that the type of the reference is not of resource kind. -- At the end of a function (when `Ret` is reached), no local whose type is of resource kind must be empty, i.e., the value must have been moved out of the local. - -As mentioned above, this last rule around `Ret` implies that the resource _must_ have been either: - -- Moved to global storage via `MoveTo`. -- Destroyed via `Unpack`. - -Both `MoveTo` and `Unpack` are internal to the module in which the resource is declared. - -## Reference Safety - -References are first-class in the bytecode language. Fresh references become available to a function in several ways: - -- Inputting parameters. -- Taking the address of the value in a local variable. -- Taking the address of the globally published value in an address. -- Taking the address of a field from a reference to the containing struct. -- Returning value from a function. - -The goal of reference safety checking is to ensure that there are no dangling references. Here are some examples of dangling references: - -- Local variable `y` contains a reference to the value in a local variable `x`; `x` is then moved. -- Local variable `y` contains a reference to the value in a local variable `x`; `x` is then bound to a new value. -- Reference is taken to a local variable that has not been initialized. -- Reference to a value in a local variable is returned from a function. -- Reference `r` is taken to a globally published value `v`; `v` is then unpublished. - -References can be either exclusive or shared; the latter allow read-only access. A secondary goal of reference safety checking is to ensure that in the execution context of the bytecode program, including the entire evaluation stack and all function frames, if there are two distinct storage locations containing references `r1` and `r2` such that `r2` extends `r1`, then both of the following conditions hold: - -- If `r1` is tagged as exclusive, then it must be inactive, i.e. it is impossible to reach a control location where `r1` is dereferenced or mutated. -- If `r1` is shared, then `r2` is shared. - -The two conditions above establish the property of referential transparency, important for scalable program verification, which looks roughly as follows: consider the piece of code `v1 = *r; S; v2 = *r`, where `S` is an arbitrary computation that does not perform any write through the syntactic reference `r` (and no writes to any `r'` that extends `r`). Then `v1 == v2`. - -### Analysis Setup - -The reference safety analysis is set up as a flow analysis (or abstract interpretation). An abstract state is defined for abstractly executing the code of a basic block. A map is maintained from basic blocks to abstract states. Given an abstract state _S_ at the beginning of a basic block _B_, the abstract execution of _B_ results in state _S'_. This state _S'_ is propagated to all successors of _B_ and recorded in the map. If a state already existed for a block, the freshly propagated state is “joined” with the existing state. If the join fails an error is reported. If the join succeeds but the abstract state remains unchanged, no further propagation is done. Otherwise, the state is updated and propagated again through the block. An error may also be reported when an instruction is processed during the propagation of abstract state through a block. - -**Errors.** As mentioned earlier, an error is reported by the checker in one of the following situations: - -- An instruction cannot be proven to be safe during the propagation of the abstract state through a block. -- Join of abstract states propagated via different incoming edges into a block fails. - -## How is this module organized? - -```text -* -├── invalid-mutations # Library used by proptests -├── src # Core bytecode verifier files -├── tests # Proptests -``` diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_cache.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_cache.rs deleted file mode 100644 index 9f9a61b9329..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_cache.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use move_binary_format::{ - errors::PartialVMResult, - file_format::{AbilitySet, DatatypeHandleIndex, SignatureToken}, - safe_unwrap, CompiledModule, -}; -use move_bytecode_verifier_meter::{Meter, Scope}; -use std::{ - cmp::max, - collections::{btree_map::Entry, BTreeMap}, -}; - -const TYPE_ARG_COST: u128 = 1; - -pub struct AbilityCache<'a> { - module: &'a CompiledModule, - vector_results: BTreeMap, - datatype_results: BTreeMap, AbilitySet>>, -} - -impl<'a> AbilityCache<'a> { - pub fn new(module: &'a CompiledModule) -> Self { - Self { - module, - vector_results: BTreeMap::new(), - datatype_results: BTreeMap::new(), - } - } - - pub fn abilities( - &mut self, - scope: Scope, - meter: &mut (impl Meter + ?Sized), - type_parameter_abilities: &[AbilitySet], - ty: &SignatureToken, - ) -> PartialVMResult { - use SignatureToken as S; - - Ok(match ty { - S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => { - AbilitySet::PRIMITIVES - } - - S::Reference(_) | S::MutableReference(_) => AbilitySet::REFERENCES, - S::Signer => AbilitySet::SIGNER, - S::TypeParameter(idx) => *safe_unwrap!(type_parameter_abilities.get(*idx as usize)), - S::Datatype(idx) => { - let sh = self.module.datatype_handle_at(*idx); - sh.abilities - } - S::Vector(inner) => { - let inner_abilities = - self.abilities(scope, meter, type_parameter_abilities, inner)?; - let entry = self.vector_results.entry(inner_abilities); - match entry { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - meter.add(scope, TYPE_ARG_COST)?; - let abilities = AbilitySet::polymorphic_abilities( - AbilitySet::VECTOR, - vec![false], - vec![inner_abilities], - )?; - entry.insert(abilities); - abilities - } - } - } - S::DatatypeInstantiation(inst) => { - let (idx, type_args) = &**inst; - let type_arg_abilities = type_args - .iter() - .map(|arg| self.abilities(scope, meter, type_parameter_abilities, arg)) - .collect::>>()?; - let entry = self - .datatype_results - .entry(*idx) - .or_default() - .entry(type_arg_abilities.clone()); - match entry { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - meter.add_items(scope, TYPE_ARG_COST, max(type_args.len(), 1))?; - let sh = self.module.datatype_handle_at(*idx); - let declared_abilities = sh.abilities; - let abilities = AbilitySet::polymorphic_abilities( - declared_abilities, - sh.type_parameters.iter().map(|param| param.is_phantom), - type_arg_abilities, - )?; - entry.insert(abilities); - abilities - } - } - } - }) - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_field_requirements.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_field_requirements.rs deleted file mode 100644 index a7dd938c6eb..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/ability_field_requirements.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements a checker for verifying that all of the struct's -//! fields satisfy the abilities required by the struct's abilities -use move_binary_format::{ - errors::{verification_error, Location, PartialVMResult, VMResult}, - file_format::{AbilitySet, CompiledModule, StructFieldInformation, TableIndex}, - IndexKind, -}; -use move_bytecode_verifier_meter::{Meter, Scope}; -use move_core_types::vm_status::StatusCode; - -use crate::ability_cache::AbilityCache; - -pub fn verify_module<'env>( - module: &'env CompiledModule, - ability_cache: &mut AbilityCache<'env>, - meter: &mut (impl Meter + ?Sized), -) -> VMResult<()> { - verify_module_impl(module, ability_cache, meter) - .map_err(|e| e.finish(Location::Module(module.self_id()))) -} - -fn verify_module_impl<'env>( - module: &'env CompiledModule, - ability_cache: &mut AbilityCache<'env>, - meter: &mut (impl Meter + ?Sized), -) -> PartialVMResult<()> { - for (idx, struct_def) in module.struct_defs().iter().enumerate() { - let sh = module.datatype_handle_at(struct_def.struct_handle); - let fields = match &struct_def.field_information { - StructFieldInformation::Native => continue, - StructFieldInformation::Declared(fields) => fields, - }; - let required_abilities = sh - .abilities - .into_iter() - .map(|a| a.requires()) - .fold(AbilitySet::EMPTY, |acc, required| acc | required); - // Assume type parameters have all abilities, as the struct's abilities will be - // dependent on them - let type_parameter_abilities = sh - .type_parameters - .iter() - .map(|_| AbilitySet::ALL) - .collect::>(); - for field in fields { - let field_abilities = ability_cache.abilities( - Scope::Module, - meter, - &type_parameter_abilities, - &field.signature.0, - )?; - if !required_abilities.is_subset(field_abilities) { - return Err(verification_error( - StatusCode::FIELD_MISSING_TYPE_ABILITY, - IndexKind::StructDefinition, - idx as TableIndex, - )); - } - } - } - - for (idx, enum_def) in module.enum_defs().iter().enumerate() { - let sh = module.datatype_handle_at(enum_def.enum_handle); - let required_abilities = sh - .abilities - .into_iter() - .map(|a| a.requires()) - .fold(AbilitySet::EMPTY, |acc, required| acc | required); - // Assume type parameters have all abilities, as the enum's abilities will be - // dependent on them - let type_parameter_abilities = sh - .type_parameters - .iter() - .map(|_| AbilitySet::ALL) - .collect::>(); - for (i, variant) in enum_def.variants.iter().enumerate() { - for (fi, field) in variant.fields.iter().enumerate() { - let field_abilities = ability_cache.abilities( - Scope::Module, - meter, - &type_parameter_abilities, - &field.signature.0, - )?; - if !required_abilities.is_subset(field_abilities) { - return Err(verification_error( - StatusCode::FIELD_MISSING_TYPE_ABILITY, - IndexKind::EnumDefinition, - idx as TableIndex, - ) - .at_index(IndexKind::VariantTag, i as TableIndex) - .at_index(IndexKind::FieldDefinition, fi as TableIndex)); - } - } - } - } - Ok(()) -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/acquires_list_verifier.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/acquires_list_verifier.rs deleted file mode 100644 index 9c94d58cd8a..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/acquires_list_verifier.rs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements a checker for verifying properties about the acquires list on function -//! definitions. Function definitions must annotate the global resources (declared in that module) -//! accesssed by `BorrowGlobal`, `MoveFrom`, and any transitive function calls -//! The list of acquired resources (stored in `FunctionDefinition`'s `acquires_global_resources` -//! field) must have: -//! - No duplicate resources (checked by `check_duplication`) -//! - No missing resources (any resource acquired must be present) -//! - No additional resources (no extraneous resources not actually acquired) - -use std::collections::{BTreeSet, HashMap}; - -use move_binary_format::{ - errors::{PartialVMError, PartialVMResult}, - file_format::{ - Bytecode, CodeOffset, CompiledModule, FunctionDefinition, FunctionDefinitionIndex, - FunctionHandle, FunctionHandleIndex, StructDefinitionIndex, - }, - safe_unwrap, -}; -use move_bytecode_verifier_meter::Meter; -use move_core_types::vm_status::StatusCode; - -pub(crate) struct AcquiresVerifier<'a> { - module: &'a CompiledModule, - current_function: FunctionDefinitionIndex, - annotated_acquires: BTreeSet, - actual_acquires: BTreeSet, - handle_to_def: HashMap, -} - -impl<'a> AcquiresVerifier<'a> { - pub(crate) fn verify( - module: &'a CompiledModule, - index: FunctionDefinitionIndex, - function_definition: &'a FunctionDefinition, - _meter: &mut (impl Meter + ?Sized), // currently unused - ) -> PartialVMResult<()> { - let annotated_acquires: BTreeSet<_> = function_definition - .acquires_global_resources - .iter() - .cloned() - .collect(); - let mut handle_to_def = HashMap::new(); - for func_def in module.function_defs() { - handle_to_def.insert(func_def.function, func_def); - } - let mut verifier = Self { - module, - current_function: index, - annotated_acquires, - actual_acquires: BTreeSet::new(), - handle_to_def, - }; - - for (offset, instruction) in safe_unwrap!(function_definition.code.as_ref()) - .code - .iter() - .enumerate() - { - verifier.verify_instruction(instruction, offset as CodeOffset)? - } - - for annotation in verifier.annotated_acquires { - if !verifier.actual_acquires.contains(&annotation) { - return Err(PartialVMError::new( - StatusCode::EXTRANEOUS_ACQUIRES_ANNOTATION, - )); - } - - let struct_def = safe_unwrap!(module.struct_defs().get(annotation.0 as usize)); - let struct_handle = module.datatype_handle_at(struct_def.struct_handle); - if !struct_handle.abilities.has_key() { - return Err(PartialVMError::new(StatusCode::INVALID_ACQUIRES_ANNOTATION)); - } - } - - Ok(()) - } - - fn verify_instruction( - &mut self, - instruction: &Bytecode, - offset: CodeOffset, - ) -> PartialVMResult<()> { - match instruction { - Bytecode::Call(idx) => self.call_acquire(*idx, offset), - Bytecode::CallGeneric(idx) => { - let fi = self.module.function_instantiation_at(*idx); - self.call_acquire(fi.handle, offset) - } - Bytecode::MoveFromDeprecated(idx) - | Bytecode::MutBorrowGlobalDeprecated(idx) - | Bytecode::ImmBorrowGlobalDeprecated(idx) => self.struct_acquire(*idx, offset), - Bytecode::MoveFromGenericDeprecated(idx) - | Bytecode::MutBorrowGlobalGenericDeprecated(idx) - | Bytecode::ImmBorrowGlobalGenericDeprecated(idx) => { - let si = self.module.struct_instantiation_at(*idx); - self.struct_acquire(si.def, offset) - } - - Bytecode::Pop - | Bytecode::BrTrue(_) - | Bytecode::BrFalse(_) - | Bytecode::Abort - | Bytecode::Branch(_) - | Bytecode::Nop - | Bytecode::Ret - | Bytecode::StLoc(_) - | Bytecode::MoveLoc(_) - | Bytecode::CopyLoc(_) - | Bytecode::ImmBorrowLoc(_) - | Bytecode::MutBorrowLoc(_) - | Bytecode::FreezeRef - | Bytecode::MutBorrowField(_) - | Bytecode::MutBorrowFieldGeneric(_) - | Bytecode::ImmBorrowField(_) - | Bytecode::ImmBorrowFieldGeneric(_) - | Bytecode::LdU8(_) - | Bytecode::LdU16(_) - | Bytecode::LdU32(_) - | Bytecode::LdU64(_) - | Bytecode::LdU128(_) - | Bytecode::LdU256(_) - | Bytecode::LdConst(_) - | Bytecode::LdTrue - | Bytecode::LdFalse - | Bytecode::Pack(_) - | Bytecode::PackGeneric(_) - | Bytecode::Unpack(_) - | Bytecode::UnpackGeneric(_) - | Bytecode::ReadRef - | Bytecode::WriteRef - | Bytecode::CastU8 - | Bytecode::CastU16 - | Bytecode::CastU32 - | Bytecode::CastU64 - | Bytecode::CastU128 - | Bytecode::CastU256 - | Bytecode::Add - | Bytecode::Sub - | Bytecode::Mul - | Bytecode::Mod - | Bytecode::Div - | Bytecode::BitOr - | Bytecode::BitAnd - | Bytecode::Xor - | Bytecode::Shl - | Bytecode::Shr - | Bytecode::Or - | Bytecode::And - | Bytecode::Not - | Bytecode::Eq - | Bytecode::Neq - | Bytecode::Lt - | Bytecode::Gt - | Bytecode::Le - | Bytecode::Ge - | Bytecode::ExistsDeprecated(_) - | Bytecode::ExistsGenericDeprecated(_) - | Bytecode::MoveToDeprecated(_) - | Bytecode::MoveToGenericDeprecated(_) - | Bytecode::VecPack(..) - | Bytecode::VecLen(_) - | Bytecode::VecImmBorrow(_) - | Bytecode::VecMutBorrow(_) - | Bytecode::VecPushBack(_) - | Bytecode::VecPopBack(_) - | Bytecode::VecUnpack(..) - | Bytecode::VecSwap(_) - | Bytecode::PackVariant(_) - | Bytecode::PackVariantGeneric(_) - | Bytecode::UnpackVariant(_) - | Bytecode::UnpackVariantImmRef(_) - | Bytecode::UnpackVariantMutRef(_) - | Bytecode::UnpackVariantGeneric(_) - | Bytecode::UnpackVariantGenericImmRef(_) - | Bytecode::UnpackVariantGenericMutRef(_) - | Bytecode::VariantSwitch(_) => Ok(()), - } - } - - fn call_acquire( - &mut self, - fh_idx: FunctionHandleIndex, - offset: CodeOffset, - ) -> PartialVMResult<()> { - let function_handle = self.module.function_handle_at(fh_idx); - let mut function_acquired_resources = - self.function_acquired_resources(function_handle, fh_idx); - for acquired_resource in &function_acquired_resources { - if !self.annotated_acquires.contains(acquired_resource) { - return Err(self.error(StatusCode::MISSING_ACQUIRES_ANNOTATION, offset)); - } - } - self.actual_acquires - .append(&mut function_acquired_resources); - Ok(()) - } - - fn struct_acquire( - &mut self, - sd_idx: StructDefinitionIndex, - offset: CodeOffset, - ) -> PartialVMResult<()> { - if self.annotated_acquires.contains(&sd_idx) { - self.actual_acquires.insert(sd_idx); - Ok(()) - } else { - Err(self.error(StatusCode::MISSING_ACQUIRES_ANNOTATION, offset)) - } - } - - fn function_acquired_resources( - &self, - function_handle: &FunctionHandle, - fh_idx: FunctionHandleIndex, - ) -> BTreeSet { - if function_handle.module != self.module.self_handle_idx() { - return BTreeSet::new(); - } - match self.handle_to_def.get(&fh_idx) { - Some(func_def) => func_def.acquires_global_resources.iter().cloned().collect(), - None => BTreeSet::new(), - } - } - - fn error(&self, status: StatusCode, offset: CodeOffset) -> PartialVMError { - PartialVMError::new(status).at_code_offset(self.current_function, offset) - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/check_duplication.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/check_duplication.rs deleted file mode 100644 index cf320d91782..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/check_duplication.rs +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements a checker for verifying that each vector in a -//! CompiledModule contains distinct values. Successful verification implies -//! that an index in vector can be used to uniquely name the entry at that -//! index. Additionally, the checker also verifies the following: -//! - struct and field definitions are consistent -//! - the handles in struct and function definitions point to the self module -//! index -//! - all struct and function handles pointing to the self module index have a -//! definition -use std::{collections::HashSet, hash::Hash}; - -use move_binary_format::{ - errors::{verification_error, Location, PartialVMResult, VMResult}, - file_format::{ - CompiledModule, Constant, DatatypeHandle, DatatypeHandleIndex, FunctionHandle, - FunctionHandleIndex, FunctionInstantiation, ModuleHandle, Signature, - StructFieldInformation, TableIndex, VariantHandle, - }, - IndexKind, -}; -use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode, -}; - -pub struct DuplicationChecker<'a> { - module: &'a CompiledModule, -} - -impl<'a> DuplicationChecker<'a> { - pub fn verify_module(module: &'a CompiledModule) -> VMResult<()> { - Self::verify_module_impl(module).map_err(|e| e.finish(Location::Module(module.self_id()))) - } - - fn verify_module_impl(module: &'a CompiledModule) -> PartialVMResult<()> { - Self::check_identifiers(module.identifiers())?; - Self::check_address_identifiers(module.address_identifiers())?; - Self::check_constants(module.constant_pool())?; - Self::check_signatures(module.signatures())?; - Self::check_module_handles(module.module_handles())?; - Self::check_module_handles(module.friend_decls())?; - Self::check_datatype_handles(module.datatype_handles())?; - Self::check_function_handles(module.function_handles())?; - Self::check_function_instantiations(module.function_instantiations())?; - Self::check_variant_handles(module.variant_handles())?; - - let checker = Self { module }; - checker.check_field_handles()?; - checker.check_field_instantiations()?; - checker.check_function_definitions()?; - checker.check_struct_definitions()?; - checker.check_struct_instantiations()?; - checker.check_enum_definitions()?; - checker.check_enum_instantiations()?; - checker.check_datatype_handles_implemented() - } - - fn check_identifiers(identifiers: &[Identifier]) -> PartialVMResult<()> { - match Self::first_duplicate_element(identifiers) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::Identifier, - idx, - )), - None => Ok(()), - } - } - - fn check_address_identifiers(address_identifiers: &[AccountAddress]) -> PartialVMResult<()> { - match Self::first_duplicate_element(address_identifiers) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::AddressIdentifier, - idx, - )), - None => Ok(()), - } - } - - fn check_constants(constant_pool: &[Constant]) -> PartialVMResult<()> { - match Self::first_duplicate_element(constant_pool) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::ConstantPool, - idx, - )), - None => Ok(()), - } - } - - fn check_signatures(signatures: &[Signature]) -> PartialVMResult<()> { - match Self::first_duplicate_element(signatures) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::Signature, - idx, - )), - None => Ok(()), - } - } - - fn check_module_handles(module_handles: &[ModuleHandle]) -> PartialVMResult<()> { - match Self::first_duplicate_element(module_handles) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::ModuleHandle, - idx, - )), - None => Ok(()), - } - } - - // DatatypeHandles - module and name define uniqueness - fn check_datatype_handles(datatype_handles: &[DatatypeHandle]) -> PartialVMResult<()> { - match Self::first_duplicate_element(datatype_handles.iter().map(|x| (x.module, x.name))) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::DatatypeHandle, - idx, - )), - None => Ok(()), - } - } - - fn check_variant_handles(variant_handles: &[VariantHandle]) -> PartialVMResult<()> { - match Self::first_duplicate_element(variant_handles.iter().map(|x| (x.enum_def, x.variant))) - { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::VariantHandle, - idx, - )), - None => Ok(()), - } - } - - fn check_function_instantiations( - function_instantiations: &[FunctionInstantiation], - ) -> PartialVMResult<()> { - match Self::first_duplicate_element(function_instantiations) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FunctionInstantiation, - idx, - )), - None => Ok(()), - } - } - - // FunctionHandles - module and name define uniqueness - fn check_function_handles(function_handles: &[FunctionHandle]) -> PartialVMResult<()> { - match Self::first_duplicate_element(function_handles.iter().map(|x| (x.module, x.name))) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FunctionHandle, - idx, - )), - None => Ok(()), - } - } - - // Module only code - // - - fn check_field_handles(&self) -> PartialVMResult<()> { - match Self::first_duplicate_element(self.module.field_handles()) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FieldHandle, - idx, - )), - None => Ok(()), - } - } - - fn check_struct_instantiations(&self) -> PartialVMResult<()> { - match Self::first_duplicate_element(self.module.struct_instantiations()) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::StructDefInstantiation, - idx, - )), - None => Ok(()), - } - } - - fn check_enum_instantiations(&self) -> PartialVMResult<()> { - match Self::first_duplicate_element(self.module.enum_instantiations()) { - Some(idx) => Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::EnumDefInstantiation, - idx, - )), - None => Ok(()), - } - } - - fn check_field_instantiations(&self) -> PartialVMResult<()> { - if let Some(idx) = Self::first_duplicate_element(self.module.field_instantiations()) { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FieldInstantiation, - idx, - )); - } - Ok(()) - } - - fn check_struct_definitions(&self) -> PartialVMResult<()> { - // StructDefinition - contained DatatypeHandle defines uniqueness - if let Some(idx) = - Self::first_duplicate_element(self.module.struct_defs().iter().map(|x| x.struct_handle)) - { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::StructDefinition, - idx, - )); - } - // Field names in structs must be unique - for (struct_idx, struct_def) in self.module.struct_defs().iter().enumerate() { - let fields = match &struct_def.field_information { - StructFieldInformation::Native => continue, - StructFieldInformation::Declared(fields) => fields, - }; - if fields.is_empty() { - return Err(verification_error( - StatusCode::ZERO_SIZED_STRUCT, - IndexKind::StructDefinition, - struct_idx as TableIndex, - )); - } - if let Some(idx) = Self::first_duplicate_element(fields.iter().map(|x| x.name)) { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FieldDefinition, - idx, - )); - } - } - // Check that each struct definition is pointing to the self module - if let Some(idx) = self.module.struct_defs().iter().position(|x| { - self.module.datatype_handle_at(x.struct_handle).module != self.module.self_handle_idx() - }) { - return Err(verification_error( - StatusCode::INVALID_MODULE_HANDLE, - IndexKind::StructDefinition, - idx as TableIndex, - )); - } - Ok(()) - } - - fn check_datatype_handles_implemented(&self) -> PartialVMResult<()> { - let implemented_datatype_handles: HashSet = self - .module - .struct_defs() - .iter() - .map(|x| x.struct_handle) - .chain(self.module.enum_defs().iter().map(|x| x.enum_handle)) - .collect(); - if let Some(idx) = (0..self.module.datatype_handles().len()).position(|x| { - let y = DatatypeHandleIndex::new(x as u16); - self.module.datatype_handle_at(y).module == self.module.self_handle_idx() - && !implemented_datatype_handles.contains(&y) - }) { - return Err(verification_error( - StatusCode::UNIMPLEMENTED_HANDLE, - IndexKind::DatatypeHandle, - idx as TableIndex, - )); - } - Ok(()) - } - - fn check_enum_definitions(&self) -> PartialVMResult<()> { - // EnumDefinition - contained DatatypeHandle defines uniqueness - // NB: We check uniqueness across both enum and struct handles at this point to - // make sure data definitions are not duplicated across struct and - // enums. - if let Some(idx) = Self::first_duplicate_element( - self.module - .struct_defs() - .iter() - .map(|x| x.struct_handle) - .chain(self.module.enum_defs().iter().map(|x| x.enum_handle)), - ) { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::EnumDefinition, - idx, - )); - } - // Variant names in enums must be unique - // Field names in each variant must be unique - for (enum_idx, enum_def) in self.module.enum_defs().iter().enumerate() { - let variants = &enum_def.variants; - if variants.is_empty() { - return Err(verification_error( - StatusCode::ZERO_SIZED_ENUM, - IndexKind::EnumDefinition, - enum_idx as TableIndex, - )); - } - if let Some(idx) = - Self::first_duplicate_element(variants.iter().map(|x| x.variant_name)) - { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::EnumDefinition, - enum_idx as TableIndex, - ) - .at_index(IndexKind::VariantTag, idx as TableIndex)); - } - - // NB: we allow zero-sized variants: since we require non-empty enums we always - // have a tag and therefore a variant with no fields is still - // non-zero sized whereas a struct with zero fields is zero-sized. - for (tag, variant) in variants.iter().enumerate() { - if let Some(idx) = - Self::first_duplicate_element(variant.fields.iter().map(|x| x.name)) - { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FieldDefinition, - idx, - ) - .at_index(IndexKind::VariantTag, tag as TableIndex) - .at_index(IndexKind::EnumDefinition, enum_idx as TableIndex)); - } - } - } - // Check that each enum definition is pointing to the self module - if let Some(idx) = self.module.enum_defs().iter().position(|x| { - self.module.datatype_handle_at(x.enum_handle).module != self.module.self_handle_idx() - }) { - return Err(verification_error( - StatusCode::INVALID_MODULE_HANDLE, - IndexKind::EnumDefinition, - idx as TableIndex, - )); - } - Ok(()) - } - - fn check_function_definitions(&self) -> PartialVMResult<()> { - // FunctionDefinition - contained FunctionHandle defines uniqueness - if let Some(idx) = - Self::first_duplicate_element(self.module.function_defs().iter().map(|x| x.function)) - { - return Err(verification_error( - StatusCode::DUPLICATE_ELEMENT, - IndexKind::FunctionDefinition, - idx, - )); - } - // Acquires in function declarations contain unique struct definitions - for (idx, function_def) in self.module.function_defs().iter().enumerate() { - let acquires = function_def.acquires_global_resources.iter(); - if Self::first_duplicate_element(acquires).is_some() { - return Err(verification_error( - StatusCode::DUPLICATE_ACQUIRES_ANNOTATION, - IndexKind::FunctionDefinition, - idx as TableIndex, - )); - } - } - // Check that each function definition is pointing to the self module - if let Some(idx) = self.module.function_defs().iter().position(|x| { - self.module.function_handle_at(x.function).module != self.module.self_handle_idx() - }) { - return Err(verification_error( - StatusCode::INVALID_MODULE_HANDLE, - IndexKind::FunctionDefinition, - idx as TableIndex, - )); - } - // Check that each function handle in self module is implemented (has a - // declaration) - let implemented_function_handles: HashSet = self - .module - .function_defs() - .iter() - .map(|x| x.function) - .collect(); - if let Some(idx) = (0..self.module.function_handles().len()).position(|x| { - let y = FunctionHandleIndex::new(x as u16); - self.module.function_handle_at(y).module == self.module.self_handle_idx() - && !implemented_function_handles.contains(&y) - }) { - return Err(verification_error( - StatusCode::UNIMPLEMENTED_HANDLE, - IndexKind::FunctionHandle, - idx as TableIndex, - )); - } - Ok(()) - } - - fn first_duplicate_element(iter: T) -> Option - where - T: IntoIterator, - T::Item: Eq + Hash, - { - let mut uniq = HashSet::new(); - for (i, x) in iter.into_iter().enumerate() { - if !uniq.insert(x) { - return Some(i as TableIndex); - } - } - None - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/code_unit_verifier.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/code_unit_verifier.rs deleted file mode 100644 index 8e4a1eebe54..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/code_unit_verifier.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements the checker for verifying correctness of function bodies. -//! The overall verification is split between stack_usage_verifier.rs and -//! abstract_interpreter.rs. CodeUnitVerifier simply orchestrates calls into these two files. -use crate::{ - ability_cache::AbilityCache, acquires_list_verifier::AcquiresVerifier, control_flow, - locals_safety, reference_safety, stack_usage_verifier::StackUsageVerifier, type_safety, -}; -use move_abstract_interpreter::{absint::FunctionContext, control_flow_graph::ControlFlowGraph}; -use move_binary_format::{ - errors::{Location, PartialVMError, PartialVMResult, VMResult}, - file_format::{ - CompiledModule, FunctionDefinition, FunctionDefinitionIndex, IdentifierIndex, TableIndex, - }, - IndexKind, -}; -use move_bytecode_verifier_meter::{Meter, Scope}; -use move_core_types::vm_status::StatusCode; -use move_vm_config::verifier::VerifierConfig; -use std::collections::HashMap; - -pub struct CodeUnitVerifier<'env, 'a> { - module: &'env CompiledModule, - function_context: FunctionContext<'env>, - name_def_map: &'a HashMap, -} - -pub fn verify_module<'env>( - verifier_config: &VerifierConfig, - module: &'env CompiledModule, - ability_cache: &mut AbilityCache<'env>, - meter: &mut (impl Meter + ?Sized), -) -> VMResult<()> { - verify_module_impl(verifier_config, module, ability_cache, meter) - .map_err(|e| e.finish(Location::Module(module.self_id()))) -} - -fn verify_module_impl<'env>( - verifier_config: &VerifierConfig, - module: &'env CompiledModule, - ability_cache: &mut AbilityCache<'env>, - meter: &mut (impl Meter + ?Sized), -) -> PartialVMResult<()> { - let mut name_def_map = HashMap::new(); - for (idx, func_def) in module.function_defs().iter().enumerate() { - let fh = module.function_handle_at(func_def.function); - name_def_map.insert(fh.name, FunctionDefinitionIndex(idx as u16)); - } - let mut total_back_edges = 0; - for (idx, function_definition) in module.function_defs().iter().enumerate() { - let index = FunctionDefinitionIndex(idx as TableIndex); - let num_back_edges = verify_function( - verifier_config, - index, - function_definition, - module, - ability_cache, - &name_def_map, - meter, - ) - .map_err(|err| err.at_index(IndexKind::FunctionDefinition, index.0))?; - total_back_edges += num_back_edges; - } - if let Some(limit) = verifier_config.max_back_edges_per_module { - if total_back_edges > limit { - return Err(PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES)); - } - } - Ok(()) -} - -fn verify_function<'env>( - verifier_config: &VerifierConfig, - index: FunctionDefinitionIndex, - function_definition: &'env FunctionDefinition, - module: &'env CompiledModule, - ability_cache: &mut AbilityCache<'env>, - name_def_map: &HashMap, - meter: &mut (impl Meter + ?Sized), -) -> PartialVMResult { - meter.enter_scope( - module - .identifier_at(module.function_handle_at(function_definition.function).name) - .as_str(), - Scope::Function, - ); - // nothing to verify for native function - let code = match &function_definition.code { - Some(code) => code, - None => return Ok(0), - }; - - // create `FunctionContext` and `BinaryIndexedView` - let function_context = control_flow::verify_function( - verifier_config, - module, - index, - function_definition, - code, - meter, - )?; - - if let Some(limit) = verifier_config.max_basic_blocks { - if function_context.cfg().blocks().len() > limit { - return Err( - PartialVMError::new(StatusCode::TOO_MANY_BASIC_BLOCKS).at_code_offset(index, 0) - ); - } - } - - let num_back_edges = function_context.cfg().num_back_edges(); - if let Some(limit) = verifier_config.max_back_edges_per_function { - if num_back_edges > limit { - return Err( - PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES).at_code_offset(index, 0) - ); - } - } - - // verify - let code_unit_verifier = CodeUnitVerifier { - module, - function_context, - name_def_map, - }; - code_unit_verifier.verify_common(verifier_config, ability_cache, meter)?; - AcquiresVerifier::verify(module, index, function_definition, meter)?; - - meter.transfer(Scope::Function, Scope::Module, 1.0)?; - - Ok(num_back_edges) -} - -impl<'env, 'a> CodeUnitVerifier<'env, 'a> { - fn verify_common( - &self, - verifier_config: &VerifierConfig, - ability_cache: &mut AbilityCache<'env>, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult<()> { - StackUsageVerifier::verify(verifier_config, self.module, &self.function_context, meter)?; - type_safety::verify(self.module, &self.function_context, ability_cache, meter)?; - locals_safety::verify(self.module, &self.function_context, ability_cache, meter)?; - reference_safety::verify( - self.module, - &self.function_context, - self.name_def_map, - meter, - ) - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/constants.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/constants.rs deleted file mode 100644 index fe14ea9eb3f..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/constants.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements a checker for verifying that -//! - a constant's type only refers to primitive types -//! - a constant's data serializes correctly for that type -use move_binary_format::{ - errors::{verification_error, Location, PartialVMResult, VMResult}, - file_format::{CompiledModule, Constant, SignatureToken, TableIndex}, - IndexKind, -}; -use move_core_types::vm_status::StatusCode; - -pub fn verify_module(module: &CompiledModule) -> VMResult<()> { - verify_module_impl(module).map_err(|e| e.finish(Location::Module(module.self_id()))) -} - -fn verify_module_impl(module: &CompiledModule) -> PartialVMResult<()> { - for (idx, constant) in module.constant_pool().iter().enumerate() { - verify_constant(idx, constant)? - } - Ok(()) -} - -fn verify_constant(idx: usize, constant: &Constant) -> PartialVMResult<()> { - verify_constant_type(idx, &constant.type_)?; - verify_constant_data(idx, constant) -} - -fn verify_constant_type(idx: usize, type_: &SignatureToken) -> PartialVMResult<()> { - if type_.is_valid_for_constant() { - Ok(()) - } else { - Err(verification_error( - StatusCode::INVALID_CONSTANT_TYPE, - IndexKind::ConstantPool, - idx as TableIndex, - )) - } -} - -fn verify_constant_data(idx: usize, constant: &Constant) -> PartialVMResult<()> { - match constant.deserialize_constant() { - Some(_) => Ok(()), - None => Err(verification_error( - StatusCode::MALFORMED_CONSTANT_DATA, - IndexKind::ConstantPool, - idx as TableIndex, - )), - } -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow.rs deleted file mode 100644 index e66039afd94..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements control flow checks. -//! -//! For bytecode versions 6 and up, the following properties are ensured: -//! - The CFG is not empty and the last block ends in an unconditional jump, so -//! it's not possible to fall off the end of a function. -//! - The CFG is reducible (and optionally max loop depth is bounded), to limit -//! the potential for pathologically long abstract interpretation runtimes -//! (through poor choice of loop heads and back edges). -//! -//! For bytecode versions 5 and below, delegates to `control_flow_v5`. -use crate::{ - control_flow_v5, - loop_summary::{LoopPartition, LoopSummary}, -}; -use move_abstract_interpreter::absint::FunctionContext; -use move_binary_format::{ - errors::{PartialVMError, PartialVMResult}, - file_format::{CodeOffset, CodeUnit, FunctionDefinition, FunctionDefinitionIndex}, - CompiledModule, -}; -use move_bytecode_verifier_meter::Meter; -use move_core_types::vm_status::StatusCode; -use move_vm_config::verifier::VerifierConfig; -use std::collections::BTreeSet; - -/// Perform control flow verification on the compiled function, returning its -/// `FunctionContext` if verification was successful. -pub fn verify_function<'env>( - verifier_config: &VerifierConfig, - module: &'env CompiledModule, - index: FunctionDefinitionIndex, - function_definition: &'env FunctionDefinition, - code: &'env CodeUnit, - _meter: &mut (impl Meter + ?Sized), // TODO: metering -) -> PartialVMResult> { - let function_handle = module.function_handle_at(function_definition.function); - - if module.version() <= 5 { - control_flow_v5::verify(verifier_config, Some(index), code)?; - Ok(FunctionContext::new(module, index, code, function_handle)) - } else { - verify_fallthrough(Some(index), code)?; - let function_context = FunctionContext::new(module, index, code, function_handle); - verify_reducibility(verifier_config, &function_context)?; - Ok(function_context) - } -} - -/// Check to make sure that the bytecode vector is non-empty and ends with a -/// branching instruction. -fn verify_fallthrough( - current_function_opt: Option, - code: &CodeUnit, -) -> PartialVMResult<()> { - let current_function = current_function_opt.unwrap_or(FunctionDefinitionIndex(0)); - match code.code.last() { - None => Err(PartialVMError::new(StatusCode::EMPTY_CODE_UNIT)), - Some(last) if !last.is_unconditional_branch() => { - Err(PartialVMError::new(StatusCode::INVALID_FALL_THROUGH) - .at_code_offset(current_function, (code.code.len() - 1) as CodeOffset)) - } - Some(_) => Ok(()), - } -} - -/// Test that `function_context`'s control-flow graph is reducible using -/// Tarjan's algorithm [1]. Optionally test loop depth bounded by -/// `verifier_config.max_loop_depth`. -/// -/// A CFG, `G`, with starting block `s` is reducible if and only if [2] any of -/// the following equivalent properties hold: -/// -/// 1. G has a unique set of back-edges `u -> v` where `v` dominates `u`, that -/// corresponds to the set of back-edges for any depth-first spanning tree -/// of G. -/// -/// 2. Every loop in G contains a unique node `h` (the "head") which dominates -/// all other nodes in the loop. -/// -/// 3. G has a unique maximal (in terms of number of edges) acyclic sub-graph. -/// -/// 4. G can be reduced to a CFG containing just `s` through a sequence of the -/// following two operations: a. Delete a cyclic edge `v -> v` b. For an -/// edge `e: u -> v` where `e` is the only incident edge to `v`, collapse -/// `v` into `u` by deleting `e` and `v` and replacing all `v -> w` edges -/// with `u -> w` edges. -/// -/// Reducibility means that a control-flow graph can be decomposed into a series -/// of nested loops (strongly connected subgraphs), which leads to more -/// predictable abstract interpretation performance. -/// -/// ## References -/// -/// 1. Tarjan, R. 1974. Testing Flow Graph Reducibility. -/// 2. Hecht, M. S., Ullman J. D. 1974. Characterizations of Reducible Flow -/// Graphs. -fn verify_reducibility<'a>( - verifier_config: &VerifierConfig, - function_context: &'a FunctionContext<'a>, -) -> PartialVMResult<()> { - let current_function = function_context - .index() - .unwrap_or(FunctionDefinitionIndex(0)); - let err = move |code: StatusCode, offset: CodeOffset| { - Err(PartialVMError::new(code).at_code_offset(current_function, offset)) - }; - - let summary = LoopSummary::new(function_context.cfg()); - let mut partition = LoopPartition::new(&summary); - - // Iterate through nodes in reverse pre-order so more deeply nested loops (which - // would appear later in the pre-order) are processed first. - for head in summary.preorder().rev() { - // If a node has no back edges, it is not a loop head, so doesn't need to be - // processed. - let back = summary.back_edges(head); - if back.is_empty() { - continue; - } - - // Collect the rest of the nodes in `head`'s loop, in `body`. Start with the - // nodes that jump back to the head, and grow `body` by repeatedly - // following predecessor edges until `head` is found again. - - let mut body = BTreeSet::new(); - for node in back { - let node = partition.containing_loop(*node); - - if node != head { - body.insert(node); - } - } - - let mut frontier: Vec<_> = body.iter().copied().collect(); - while let Some(node) = frontier.pop() { - for pred in summary.pred_edges(node) { - let pred = partition.containing_loop(*pred); - - // `pred` can eventually jump back to `head`, so is part of its body. If it is - // not a descendant of `head`, it implies that `head` does not - // dominate a node in its loop, therefore the CFG is not - // reducible, according to Property 1 (see doc comment). - if !summary.is_descendant(/* ancestor */ head, /* descendant */ pred) { - return err(StatusCode::INVALID_LOOP_SPLIT, summary.block(pred)); - } - - let body_extended = pred != head && body.insert(pred); - if body_extended { - frontier.push(pred); - } - } - } - - // Collapse all the nodes in `body` into `head`, so it appears as one node when - // processing outer loops (this performs a sequence of Operation 4(b), - // followed by a 4(a)). - let depth = partition.collapse_loop(head, &body); - if let Some(max_depth) = verifier_config.max_loop_depth { - if depth as usize > max_depth { - return err(StatusCode::LOOP_MAX_DEPTH_REACHED, summary.block(head)); - } - } - } - - Ok(()) -} diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs deleted file mode 100644 index ce1a55b3a14..00000000000 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This module implements a checker to verify control flow in bytecode version -//! 5 and below. The following properties are ensured: -//! - All forward jumps do not enter into the middle of a loop -//! - All "breaks" (forward, loop-exiting jumps) go to the "end" of the loop -//! - All "continues" (back jumps in a loop) are only to the current loop -use std::{collections::HashSet, convert::TryInto}; - -use move_binary_format::{ - errors::{PartialVMError, PartialVMResult}, - file_format::{Bytecode, CodeOffset, CodeUnit, FunctionDefinitionIndex}, - safe_unwrap, -}; -use move_core_types::vm_status::StatusCode; -use move_vm_config::verifier::VerifierConfig; - -pub fn verify( - verifier_config: &VerifierConfig, - current_function_opt: Option, - code: &CodeUnit, -) -> PartialVMResult<()> { - let current_function = current_function_opt.unwrap_or(FunctionDefinitionIndex(0)); - - // check fallthrough - verify_fallthrough(current_function, &code.code)?; - - // check jumps - let context = &ControlFlowVerifier { - current_function, - code: &code.code, - }; - let labels = instruction_labels(context); - check_jumps(verifier_config, context, labels) -} - -fn verify_fallthrough( - current_function: FunctionDefinitionIndex, - code: &[Bytecode], -) -> PartialVMResult<()> { - // Check to make sure that the bytecode vector ends with a branching - // instruction. - match code.last() { - None => Err(PartialVMError::new(StatusCode::EMPTY_CODE_UNIT)), - Some(last) if !last.is_unconditional_branch() => { - Err(PartialVMError::new(StatusCode::INVALID_FALL_THROUGH) - .at_code_offset(current_function, (code.len() - 1) as CodeOffset)) - } - Some(_) => Ok(()), - } -} - -#[derive(Clone, Copy)] -enum Label { - Loop { last_continue: u16 }, - Code, -} - -struct ControlFlowVerifier<'a> { - current_function: FunctionDefinitionIndex, - code: &'a Vec, -} - -impl<'a> ControlFlowVerifier<'a> { - fn code(&self) -> impl Iterator { - self.code - .iter() - .enumerate() - .map(|(idx, instr)| (idx.try_into().unwrap(), instr)) - } - - fn labeled_code<'b: 'a>( - &self, - labels: &'b [Label], - ) -> impl Iterator { - self.code() - .zip(labels) - .map(|((i, instr), lbl)| (i, instr, lbl)) - } - - fn error(&self, status: StatusCode, offset: CodeOffset) -> PartialVMError { - PartialVMError::new(status).at_code_offset(self.current_function, offset) - } -} - -fn instruction_labels(context: &ControlFlowVerifier) -> Vec