diff --git a/.github/workflows/_e2e.yml b/.github/workflows/_e2e.yml index 42eb66e2c64..21cccf3a81c 100644 --- a/.github/workflows/_e2e.yml +++ b/.github/workflows/_e2e.yml @@ -5,25 +5,22 @@ on: inputs: isRust: type: boolean - required: true + required: false isExplorer: type: boolean - required: true + required: false isAppsBackend: type: boolean - required: true + required: false isTypescriptSDK: type: boolean - required: true + required: false isWallet: type: boolean - required: true + required: false isGraphQlTransport: type: boolean - required: true - isDevelop: - type: boolean - required: true + required: false concurrency: group: e2e-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -33,7 +30,7 @@ jobs: # Run e2e test against localnet built on the develop branch localnet: name: Localnet - if: inputs.isExplorer || inputs.isTypescriptSDK || inputs.isWallet || inputs.isRust || inputs.isDevelop + if: inputs.isExplorer || inputs.isTypescriptSDK || inputs.isWallet || inputs.isRust || github.ref_name == 'develop' runs-on: self-hosted services: postgres: @@ -70,27 +67,27 @@ jobs: echo "E2E_RUN_LOCAL_NET_CMD=(RUST_LOG=info RUST_BACKTRACE=1 $(echo $PWD/target/debug/iota) start --with-faucet --force-regenesis --with-indexer --with-graphql)" >> $GITHUB_ENV - name: Run TS SDK e2e tests - if: inputs.isTypescriptSDK || inputs.isRust || inputs.isDevelop + if: inputs.isTypescriptSDK || inputs.isRust || github.ref_name == 'develop' run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @iota/iota-sdk test:e2e' - name: Run RPC/GraphQL compatibility e2e tests - if: inputs.isGraphQlTransport || inputs.isRust || inputs.isDevelop + if: inputs.isGraphQlTransport || inputs.isRust || github.ref_name == 'develop' 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 + if: inputs.isAppsBackend || github.ref_name == 'develop' run: pnpm --filter apps-backend build - name: Run apps-backend e2e tests - if: inputs.isAppsBackend || inputs.isDevelop + if: inputs.isAppsBackend || github.ref_name == 'develop' run: pnpm --filter apps-backend test:e2e - name: Build explorer - if: inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRust || inputs.isDevelop + if: inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRust || github.ref_name == 'develop' run: pnpm turbo --filter=iota-explorer build - name: Run Explorer e2e tests - if: inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRust || inputs.isDevelop + if: inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRust || github.ref_name == 'develop' run: pnpm --filter iota-explorer playwright test - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4 if: always() @@ -111,11 +108,11 @@ jobs: run: cargo run --bin iota start --force-regenesis --with-faucet --epoch-duration-ms 10000 & - name: Build Wallet - if: inputs.isWallet || inputs.isRust || inputs.isTypescriptSDK || inputs.isDevelop + if: inputs.isWallet || inputs.isRust || inputs.isTypescriptSDK || github.ref_name == 'develop' run: pnpm wallet build - name: Run Wallet e2e tests - if: inputs.isWallet || inputs.isRust || inputs.isTypescriptSDK || inputs.isDevelop + if: inputs.isWallet || inputs.isRust || inputs.isTypescriptSDK || github.ref_name == 'develop' run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- pnpm --filter iota-wallet playwright test - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4 diff --git a/.github/workflows/_external_rust_tests.yml b/.github/workflows/_external_rust_tests.yml index 1b064b0ec1b..015ff4b7676 100644 --- a/.github/workflows/_external_rust_tests.yml +++ b/.github/workflows/_external_rust_tests.yml @@ -1,6 +1,11 @@ name: Rust tests -on: workflow_call +on: + workflow_call: + inputs: + changedCrates: + type: string + required: false concurrency: group: ext-rust-tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -12,21 +17,8 @@ env: CARGO_INCREMENTAL: 0 jobs: - changes: - runs-on: [self-hosted] - outputs: - components: ${{ steps.filter.outputs.changes }} - steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4 - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3 - id: filter - with: - list-files: "json" - filters: .github/external-crates-filters.yml - test: name: Test external crates - needs: changes if: (!cancelled()) env: # Tests written with #[sim_test] are often flaky if run as #[tokio::test] - this var @@ -43,7 +35,7 @@ jobs: run: pip install pyopenssl --upgrade --break-system-packages - name: cargo test run: | - array=(${{ join(fromJson(needs.changes.outputs.components), ' ') }}) + array=(${{ inputs.changedCrates }}) result="" diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml index e9564763513..806466ae426 100644 --- a/.github/workflows/_rust.yml +++ b/.github/workflows/_rust.yml @@ -43,19 +43,47 @@ jobs: deny: uses: ./.github/workflows/_cargo_deny.yml - rust-tests: + crates-changes: + needs: rust-lints + runs-on: [self-hosted] + outputs: + components: ${{ steps.filter.outputs.changes }} + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3 + id: filter + with: + list-files: "json" + filters: .github/crates-filters.yml + + external-changes: + needs: rust-lints + runs-on: [self-hosted] + outputs: + components: ${{ steps.filter.outputs.changes }} + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3 + id: filter + with: + list-files: "json" + filters: .github/external-crates-filters.yml + + crates-tests: if: | !cancelled() && !failure() && inputs.isRust && github.event.pull_request.draft == false - needs: - - rust-lints + needs: crates-changes uses: ./.github/workflows/_rust_tests.yml + with: + changedCrates: ${{ join(fromJson(needs.crates-changes.outputs.components), ' ') }} external-tests: if: | !cancelled() && !failure() && inputs.isRust && github.event.pull_request.draft == false - needs: - - rust-lints + needs: external-changes uses: ./.github/workflows/_external_rust_tests.yml + with: + changedCrates: ${{ join(fromJson(needs.external-changes.outputs.components), ' ') }} execution-cut: if: | diff --git a/.github/workflows/_rust_tests.yml b/.github/workflows/_rust_tests.yml index 59857d2b1a8..7b2857f27fc 100644 --- a/.github/workflows/_rust_tests.yml +++ b/.github/workflows/_rust_tests.yml @@ -1,6 +1,11 @@ name: Rust tests -on: workflow_call +on: + workflow_call: + inputs: + changedCrates: + type: string + required: false concurrency: group: rust-tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -21,21 +26,8 @@ env: RUSTDOCFLAGS: -D warnings jobs: - changes: - runs-on: [self-hosted] - outputs: - components: ${{ steps.filter.outputs.changes }} - steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4 - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3 - id: filter - with: - list-files: "json" - filters: .github/crates-filters.yml - test: name: Test rust crates - needs: changes if: (!cancelled()) env: # Tests written with #[sim_test] are often flaky if run as #[tokio::test] - this var @@ -50,7 +42,7 @@ jobs: tool: nextest - name: cargo test run: | - array=(${{ join(fromJson(needs.changes.outputs.components), ' ') }}) + array=(${{ inputs.changedCrates }}) result="" @@ -126,7 +118,6 @@ jobs: simtest: name: Simtest rust - needs: changes timeout-minutes: 45 runs-on: [self-hosted] env: @@ -138,7 +129,7 @@ jobs: tool: nextest - name: setup filter run: | - array=(${{ join(fromJson(needs.changes.outputs.components), ' ') }}) + array=(${{ inputs.changedCrates }}) result="" diff --git a/.github/workflows/_vercel_deploy.yml b/.github/workflows/_vercel_deploy.yml index 2047ca432ab..d85f6664fff 100644 --- a/.github/workflows/_vercel_deploy.yml +++ b/.github/workflows/_vercel_deploy.yml @@ -21,15 +21,9 @@ on: shouldDeployPreview: type: boolean required: true - isDevelop: - type: boolean - required: true - githubRef: - type: string - required: true concurrency: - group: ${{ github.workflow }}-${{ inputs.githubRef }} + group: vercel-deploy-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: @@ -44,7 +38,7 @@ jobs: explorer-staging: name: Vercel Explorer Staging - if: inputs.isDevelop + if: github.ref_name == 'develop' uses: ./.github/workflows/apps_explorer_deploy.yml secrets: inherit with: @@ -61,7 +55,7 @@ jobs: ui-kit-prod: name: Vercel UI Kit Production - if: inputs.isDevelop + if: github.ref_name == 'develop' uses: ./.github/workflows/apps_ui_kit_deploy.yml secrets: inherit with: @@ -77,7 +71,7 @@ jobs: wallet-dashboard-prod: name: Vercel Wallet Dashboard Production - if: inputs.isDevelop + if: github.ref_name == 'develop' uses: ./.github/workflows/apps_wallet_dashboard_deploy.yml secrets: inherit with: @@ -93,7 +87,7 @@ jobs: apps-backend-prod: name: Vercel apps-backend Production - if: inputs.isDevelop + if: github.ref_name == 'develop' uses: ./.github/workflows/apps_backend_deploy.yml secrets: inherit with: diff --git a/.github/workflows/hierarchy.yml b/.github/workflows/hierarchy.yml index 612d35b03a0..309d1e93e72 100644 --- a/.github/workflows/hierarchy.yml +++ b/.github/workflows/hierarchy.yml @@ -148,7 +148,6 @@ jobs: isAppsBackend: ${{ needs.diff.outputs.isAppsBackend == 'true' }} isTypescriptSDK: ${{ needs.diff.outputs.isTypescriptSDK == 'true' }} isGraphQlTransport: ${{ needs.diff.outputs.isGraphQlTransport == 'true' }} - isDevelop: ${{ github.ref_name == 'develop' }} vercel-deploy: if: (!cancelled() && !failure()) @@ -160,14 +159,12 @@ jobs: uses: ./.github/workflows/_vercel_deploy.yml secrets: inherit with: - shouldDeployPreview: ${{github.event_name == 'pull_request' && github.event.pull_request.draft == false}} + shouldDeployPreview: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }} isExplorer: ${{ needs.diff.outputs.isExplorer == 'true' }} isTypescriptSDK: ${{ needs.diff.outputs.isTypescriptSDK == 'true' }} isAppsBackend: ${{ needs.diff.outputs.isAppsBackend == 'true' }} isAppsUiKit: ${{ needs.diff.outputs.isAppsUiKit == 'true' }} isWalletDashboard: ${{ needs.diff.outputs.isWalletDashboard == 'true' }} - isDevelop: ${{ github.ref_name == 'develop' }} - githubRef: ${{ github.event.pull_request.number || github.ref }} ledgernano: if: (!cancelled() && !failure()) && needs.diff.outputs.isLedgerjs == 'true' && github.event.pull_request.draft == false diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 4af2b2bcc34..f2b54ab7aac 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -name: Nightly binary build +name: Nightly checks on: schedule: @@ -29,11 +29,6 @@ env: RUST_BACKTRACE: short jobs: - cargo-deny-external: - uses: ./.github/workflows/_cargo_deny.yml - with: - manifest-path: external-crates/move/Cargo.toml - release: name: build release binaries runs-on: ${{ matrix.os }} @@ -72,3 +67,29 @@ jobs: with: command: build args: --all-targets --all-features --release + + tests: + uses: ./.github/workflows/_rust_tests.yml + + external-tests: + uses: ./.github/workflows/_external_rust_tests.yml + + move-tests: + uses: ./.github/workflows/_move_tests.yml + + deny: + uses: ./.github/workflows/_cargo_deny.yml + + deny-external: + uses: ./.github/workflows/_cargo_deny.yml + with: + manifest-path: external-crates/move/Cargo.toml + + e2e: + uses: ./.github/workflows/_e2e.yml + + execution-cut: + uses: ./.github/workflows/_execution_cut.yml + + split-cluster: + uses: ./.github/workflows/split_cluster.yml diff --git a/.github/workflows/split_cluster.yml b/.github/workflows/split_cluster.yml index 6da472a4073..31ce2cc9389 100644 --- a/.github/workflows/split_cluster.yml +++ b/.github/workflows/split_cluster.yml @@ -1,9 +1,16 @@ name: Split Cluster Check on: + workflow_call: pull_request: types: [opened, synchronize, reopened, ready_for_review] +env: + CARGO_TERM_COLOR: always + RUST_LOG: "error" + # Don't emit giant backtraces in the CI logs. + RUST_BACKTRACE: short + jobs: # TODO: re-enable https://github.com/iotaledger/iota/issues/3862 # validate-mainnet: diff --git a/crates/iota-indexer/database_schema.svg b/crates/iota-indexer/database_schema.svg index 86997161b05..4b785446071 100644 --- a/crates/iota-indexer/database_schema.svg +++ b/crates/iota-indexer/database_schema.svg @@ -1,4 +1,4 @@ -displayPKobject_type text NNbcs bytea NNid bytea NNversion int2 NNobjectsPKobject_id bytea NNcheckpoint_sequence_number ref(checkpoints) int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest bytea NNobject_type textobject_version in8 NNowner_id byteaowner_type int2 NNserialized_object bytea NNobjects_snapshotPKobject_id ref(objects_history) bytea NNcheckpoint_sequence_number int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest byteaobject_status int2 NNobject_type textobject_version in8 NNowner_id byteaowner_type int2serialized_object byteaobjects_historyPKcheckpoint_sequence_number ref(checkpoints) int8 NNPKobject_id ref(objects) bytea NNPKobject_version int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest byteaobject_status int2 NNobject_type textowner_id byteaowner_type int2serialized_object byteaepochsPKepoch int8 NNburn_leftover_amount int8burn_tokens_amount int8epoch_commitments byteaepoch_end_timestamp int8epoch_start_timestamp int8 NNepoch_total_transactions int8first_checkpoint_id ref(checkpoints) int8 NNlast_checkpoint_id ref(checkpoints) int8minted_tokens_amount int8protocol_version int8 NNreference_gas_price int8 NNstake_subsidy_amount int8storage_charge int8storage_fund_balance int8 NNstorage_rebate int8system_state bytea NNtotal_gas_fees int8total_stake int8 NNtotal_stake_reward_distributed int8checkpointsPKsequence_number int8 NNcheckpoint_digest bytea NNepoch int8 NNnetwork_total_transactions int8 NNprevious_checkpoint_digest byteaend_of_epoch bool NNend_of_epoch_data byteatx_digests bytea[] NNtimestamp_ms int8 NNtotal_gas_cost int8 NNcomputation_cost int8 NNstorage_cost int8 NNstorage_rebate int8 NNnon_refundable_storage_fee int8 NNcheckpoint_commitments bytea NNvalidator_signature bytea NNend_of_epoch_data byteatransactionsPKcheckpoint_sequence_number ref(checkpoints) int8 NNPKtx_sequence_number int8 NNbalance_changes bytea[] NNevents bytea[] NNobject_changes bytea[] NNraw_effects bytea NNraw_transaction bytea NNsuccess_command_count int2 NNtimestamp_ms ref(checkpoints) int8 NNtransaction_digest ref(checkpoints) bytea NNtransaction_kind int2 NNeventsPKevent_ sequence_number int8 NNPKtx_sequence_number ref(transactions) int8 NNbcs bytea NNcheckpoint_sequence_number ref(checkpoints) int8 NNevent_type text NNmodule text NNpackage bytea NNsenders bytea[] NNtimestamp_ms ref(checkpoints) int8 NNtransaction_digest ref(transactions) bytea NNtx_sendersPKsender bytea NNPKtx_sequence_number ref(transactions). int8 NNtx_recipientsPKrecipient bytea NNPKtx_sequence_number ref(transactions). int8 NNtx_callsPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNbcs bytea NNid bytea NNversion int2 NNtx_input_objectsPKobject_id bytea NNPKtx_sequence_number ref(transactions) int8 NNtx_changed_objectsPKobject_id bytea NNPKtx_sequence_number ref(transactions) int8 NNpackagesPKpackage_id bytea NNmove_package bytea NN__diesel_schema_migrationsPKversion varchar(50) NNrun_on timestamp NNLegendDatabase schemaref(< table-name >) Possible Foreign Key linktx_count_metricsPKcheckpoint_sequence_number ref(checkpoints) int8 NNepoch ref(epochs) int8 NNtimestamp_ms int8 NNtotal_transaction_blocks int8 NNtotal_successful_transaction_blocks int8 NNtotal_successful_transactions int8 NNmove_callsPKtransactions_sequence_number ref(transactions) int8 NNPKmove_package bytea NNPKmove_module text NNPKmove_function text NNcheckpoint_sequence_number ref(checkpoints) int8 NNepoch ref(epochs) int8 NNmove_call_metricsPKid bigserial NNcount int8 NNday int8 NNepoch ref(epochs) int8 NNmove_function text NNmove_module text NNmove_package text NNaddressesPKaddress bytea NNfirst_appearance_time int8 NNfirst_appearance_tx int8 NNlast_appearance_time int8 NNlast_appearance_tx int8 NNactive_addressesPKaddress bytea NNfirst_appearance_time int8 NNfirst_appearance_tx int8 NNlast_appearance_time int8 NNlast_appearance_tx int8 NNaddress_metricsPKcheckpoint ref(checkpoints) int8 NNcumulative_active_addresses int8 NNcumulative_addresses int8 NNdaily_active_addresses int8 NNepoch ref(epochs) int8 NNtimestamp_ms int8 NNepoch_peak_tpsPKepoch ref(epochs) int8 NNpeak_tps float8 NNpeak_tps_30d float8 NN \ No newline at end of file +displayPKobject_type text NNbcs bytea NNid bytea NNversion int2 NNobjectsPKobject_id bytea NNcheckpoint_sequence_number ref(checkpoints) int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest bytea NNobject_type textobject_type_module textobject_type_name textobject_type_package byteaobject_version in8 NNowner_id byteaowner_type int2 NNserialized_object bytea NNobjects_snapshotPKobject_id ref(objects_history) bytea NNcheckpoint_sequence_number ref(checkpoints) int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest byteaobject_status int2 NNobject_type textobject_type_module textobject_type_name textobject_type_package byteaobject_version in8 NNowner_id byteaowner_type int2serialized_object byteaobjects_historyPKcheckpoint_sequence_number ref(checkpoints) int8 NNPKobject_id ref(objects) bytea NNPKobject_version int8 NNcoin_balance int8coint_type textdf_kind int2df_name byteadf_object_id byteadf_object_type textobject_digest byteaobject_status int2 NNobject_type textobject_type_module textobject_type_name textobject_type_package byteaowner_id byteaowner_type int2serialized_object byteaepochsPKepoch int8 NNburn_tokens_amount int8epoch_commitments byteaepoch_end_timestamp int8epoch_start_timestamp int8 NNepoch_total_transactions int8first_checkpoint_id ref(checkpoints) int8 NNlast_checkpoint_id ref(checkpoints) int8minted_tokens_amount int8protocol_version int8 NNreference_gas_price int8 NNstorage_charge int8storage_fund_balance int8 NNstorage_rebate int8system_state bytea NNtotal_gas_fees int8total_stake int8 NNtotal_stake_reward_distributed int8checkpointsPKsequence_number int8 NNcheckpoint_commitments bytea NNcheckpoint_digest bytea NNcomputation_cost int8 NNend_of_epoch bool NNend_of_epoch_data byteaepoch int8 NNmax_tx_sequence_number int8min_tx_sequence_number int8network_total_transactions int8 NNnon_refundable_storage_fee int8 NNprevious_checkpoint_digest byteastorage_cost int8 NNstorage_rebate int8 NNtimestamp_ms int8 NNtotal_gas_cost int8 NNtx_digests bytea[] NNvalidator_signature bytea NNtransactionsPKtx_sequence_number int8 NNbalance_changes bytea[] NNcheckpoint_sequence_number ref(checkpoints) int8 NNevents bytea[] NNobject_changes bytea[] NNraw_effects bytea NNraw_transaction bytea NNsuccess_command_count int2 NNtimestamp_ms ref(checkpoints) int8 NNtransaction_digest ref(checkpoints) bytea NNtransaction_kind int2 NNeventsPKevent_ sequence_number int8 NNPKtx_sequence_number ref(transactions) int8 NNbcs bytea NNevent_type text NNmodule text NNpackage bytea NNsenders bytea[] NNtimestamp_ms ref(checkpoints) int8 NNtransaction_digest ref(transactions) bytea NNtx_sendersPKsender bytea NNPKtx_sequence_number ref(transactions) int8 NNtx_recipientsPKrecipient bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNpackagesPKoriginal_id bytea NNPKpackage_id bytea NNPKpackage_version int8 NNcheckpoint_sequence_number ref(checkpoints) int8 NNmove_package bytea NN__diesel_schema_migrationsPKversion varchar(50) NNrun_on timestamp NNLegendDatabase schemaref(< table-name >) Possible Foreign Key linkDatabase Viewtx_count_metricsPKcheckpoint_sequence_number ref(checkpoints) int8 NNepoch ref(epochs) int8 NNtimestamp_ms int8 NNtotal_successful_transaction_blocks int8 NNtotal_successful_transactions int8 NNtotal_transaction_blocks int8 NNmove_callsPKmove_function text NNPKmove_module text NNPKmove_package bytea NNPKtransactions_sequence_number ref(transactions) int8 NNcheckpoint_sequence_number ref(checkpoints) int8 NNepoch ref(epochs) int8 NNmove_call_metricsPKid bigserial NNcount int8 NNday int8 NNepoch ref(epochs) int8 NNmove_function text NNmove_module text NNmove_package text NNaddressesPKaddress bytea NNfirst_appearance_time int8 NNfirst_appearance_tx int8 NNlast_appearance_time int8 NNlast_appearance_tx int8 NNactive_addressesPKaddress bytea NNfirst_appearance_time int8 NNfirst_appearance_tx int8 NNlast_appearance_time int8 NNlast_appearance_tx int8 NNaddress_metricsPKcheckpoint ref(checkpoints) int8 NNcumulative_active_addresses int8 NNcumulative_addresses int8 NNdaily_active_addresses int8 NNepoch ref(epochs) int8 NNtimestamp_ms int8 NNepoch_peak_tpsPKepoch ref(epochs) int8 NNpeak_tps float8 NNpeak_tps_30d float8 NNchain_identifierPKcheckpoint_digest ref(checkpoints) bytea NNreal_time_tpsrecent_tps float8tx_digestsPKtx_digest bytea NNtx_sequence_number ref(transactions) int8 NNtx_kindsPKtx_kind int2 NNPKtx_sequence_number ref(transactions) int8 NNevent_sendersPKevent_sequence_number ref(events) int8 NNPKsender bytea NNPKtx_sequence_number ref(transactions) int8 NNfeature_flagsPKflag_name text NNPKprotocol_version int8 NNflag_value bool NNprotocol_configsPKconfig_name text NNPKprotocol_version int8 NNconfig_value textobjects_versionPKobject_id ref(objects) bytea NNPKobject_version ref(objects) int8 NNcp_sequence_number ref(checkpoints) int8 NNpruner_cp_watermarkPKcheckpoint_sequence_number ref(checkpoints) int8 NNPKmax_tx_sequence_number int8 NNmin_tx_sequence_number int8 NNtx_changed_objectsPKobject_id ref(objects) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNtx_input_objectsPKobject_id ref(objects) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNevent_emit_packagePKevent_sequence_number ref(events) int8 NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNevent_struct_packagePKevent_sequence_number ref(events) int8 NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNevent_emit_modulePKevent_sequence_number ref(events) int8 NNPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNnetwork_metricscurrent_checkpoint int8current_epoch int8current_tps float8total_addresses int8total_objects int8total_packages int8tps_30_days float8event_struct_instantiationPKevent_sequence_number ref(events) int8 NNPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNPKtype_instantiation text NNsender bytea NNevent_struct_namePKevent_sequence_number ref(events) int8 NNPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNPKtype_name text NNsender bytea NNtx_calls_funPKfunc text NNPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNtx_calls_modPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNtx_calls_pkgPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NNevent_struct_modulePKevent_sequence_number ref(events) int8 NNPKmodule text NNPKpackage ref(packages) bytea NNPKtx_sequence_number ref(transactions) int8 NNsender bytea NN \ No newline at end of file diff --git a/crates/iota-indexer/schema.md b/crates/iota-indexer/schema.md index 39b99baf6b2..675d030fb2b 100644 --- a/crates/iota-indexer/schema.md +++ b/crates/iota-indexer/schema.md @@ -11,7 +11,8 @@ The Indexer pulls checkpoint data from the full node and populates the tables sh > Migrations are generated by diesel cli, the basic schema can be found [schema.rs](src/schema.rs). > For more in depth understanding of the database tables, go to [migrations](migrations) folder, in the contained `SQL` the indexes, partitions & constraints are declared. > -> - Tables `objects_history` & `transactions` have partitions, each partition is created based on `checkpoint_sequence_number` (related form the `checkpoints` table) it goes from `0` to `MAXVALUE` +> - The table `objects_history` has partitions, each partition is created based on `checkpoint_sequence_number` (related from the `checkpoints` table) it goes from `0` to `MAXVALUE` +> - The table `transactions` has partitions, each partition is created based on `tx_sequence_number` (related from the `transactions` table) it goes from `0` to `MAXVALUE` > - `__diesel_schema_migrations` table is managed by `diesel` cli when applying migrations ## Indexes @@ -23,40 +24,83 @@ The Indexer pulls checkpoint data from the full node and populates the tables sh | checkpoints_epoch | epoch, sequence_number | | checkpoints_digest | checkpoint_digest | +### Table `event_emit_package` + +| Index name | Keys | +| ------------------------- | ---------------------------------------------------------- | +| event_emit_package_sender | sender, package, tx_sequence_number, event_sequence_number | + +### Table `event_emit_module` + +| Index name | Keys | +| ------------------------ | ------------------------------------------------------------------ | +| event_emit_module_sender | sender, package, module, tx_sequence_number, event_sequence_number | + +### Table `event_struct_package` + +| Index name | Keys | +| --------------------------- | ---------------------------------------------------------- | +| event_struct_package_sender | sender, package, tx_sequence_number, event_sequence_number | + +### Table `event_struct_module` + +| Index name | Keys | +| -------------------------- | ------------------------------------------------------------------ | +| event_struct_module_sender | sender, package, module, tx_sequence_number, event_sequence_number | + +### Table `event_struct_name` + +| Index name | Keys | +| ------------------------ | ----------------------------------------------------------------------------- | +| event_struct_name_sender | sender, package, module, type_name, tx_sequence_number, event_sequence_number | + +### Table `event_struct_instantiation` + +| Index name | Keys | +| --------------------------------- | -------------------------------------------------------------------------------------- | +| event_struct_instantiation_sender | sender, package, module, type_instantiation, tx_sequence_number, event_sequence_number | + ### Table `events` -| Index name | Keys | -| --------------------------------- | ----------------------------------------------------------------------- | -| events_package | package, tx_sequence_number, event_sequence_number | -| events_package_module | package, module, tx_sequence_number, event_sequence_number | -| events_event_type | event_type, text_pattern_ops, tx_sequence_number, event_sequence_number | -| events_checkpoint_sequence_number | checkpoint_sequence_number | +| Index name | Keys | +| --------------------- | ----------------------------------------------------------------------- | +| events_package | package, tx_sequence_number, event_sequence_number | +| events_package_module | package, module, tx_sequence_number, event_sequence_number | +| events_event_type | event_type, text_pattern_ops, tx_sequence_number, event_sequence_number | ### Table `objects` -| Index name | Keys | Condition | -| ---------------------------------- | -------------------------- | --------------------------------------------------------- | -| objects_owner | owner_type, owner_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | -| objects_coin | owner_id, coin_type | WHERE coin_type IS NOT NULL AND owner_type = 1 | -| objects_checkpoint_sequence_number | checkpoint_sequence_number | | -| objects_type | object_type | | +| Index name | Keys | Condition | +| ------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------- | +| objects_owner | owner_type, owner_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | +| objects_coin | owner_id, coin_type | WHERE coin_type IS NOT NULL AND owner_type = 1 | +| objects_checkpoint_sequence_number | checkpoint_sequence_number | | +| objects_package_module_name_full_type | object_type_package, object_type_module, object_type_name, object_type | | +| objects_owner_package_module_name_full_type | owner_id, object_type_package, object_type_module, object_type_name, object_type | | ### Table `objects_snapshot` -| Index name | Keys | Condition | -| ------------------------------------------- | ------------------------------- | --------------------------------------------------------- | -| objects_snapshot_checkpoint_sequence_number | checkpoint_sequence_number | | -| objects_snapshot_coin | owner_id, coin_type, object_id | WHERE coin_type IS NOT NULL AND owner_type = 1 | -| objects_snapshot_type | object_type, object_id | | -| objects_snapshot_owner | owner_type, owner_id, object_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | +| Index name | Keys | Condition | +| ---------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------- | +| objects_snapshot_checkpoint_sequence_number | checkpoint_sequence_number | | +| objects_snapshot_owner | owner_type, owner_id, object_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | +| objects_snapshot_coin_owner | owner_id, coin_type, object_id | WHERE coin_type IS NOT NULL AND owner_type = 1 | +| objects_snapshot_coin_only | coin_type, object_id | WHERE coin_type IS NOT NULL | +| objects_snapshot_type_id | object_type_package, object_type_module, object_type_name, object_type, object_id | | +| objects_snapshot_id_type | object_id, object_type_package, object_type_module, object_type_name, object_type | | +| objects_snapshot_owner_package_module_name_full_type | owner_id, object_type_package, object_type_module, object_type_name, object_type | | ### Table `objects_history` -| Index name | Keys | Condition | -| --------------------- | ------------------------------------------------ | --------------------------------------------------------- | -| objects_history_owner | checkpoint_sequence_number, owner_type, owner_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | -| objects_history_coin | checkpoint_sequence_number, owner_id, coin_type | WHERE coin_type IS NOT NULL AND owner_type = 1 | -| objects_history_type | checkpoint_sequence_number, object_type | | +| Index name | Keys | Condition | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | +| objects_history_id_version | object_id, object_version, checkpoint_sequence_number | | +| objects_history_owner | checkpoint_sequence_number, owner_type, owner_id | WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL | +| objects_history_coin_owner | checkpoint_sequence_number, owner_id, coin_type, object_id | WHERE coin_type IS NOT NULL AND owner_type = 1 | +| objects_history_coin_only | checkpoint_sequence_number, coin_type, object_id | WHERE coin_type IS NOT NULL | +| objects_history_type | checkpoint_sequence_number, object_type | | +| objects_history_package_module_name_full_type | checkpoint_sequence_number, object_type_package, object_type_module, object_type_name, object_type | | +| objects_history_owner_package_module_name_full_type | checkpoint_sequence_number, owner_id, object_type_package, object_type_module, object_type_name, object_type | | ### Table `transactions` @@ -66,25 +110,18 @@ The Indexer pulls checkpoint data from the full node and populates the tables sh | transactions_checkpoint_sequence_number | checkpoint_sequence_number | | | transactions_transaction_kind | transaction_kind | WHERE transaction_kind = 1 | -### Table `tx_calls` +### Table `tx_input_objects` -| Index name | Keys | -| --------------------------- | ----------------------------------------- | -| tx_calls_module | package, module, tx_sequence_number | -| tx_calls_func | package, module, func, tx_sequence_number | -| tx_calls_tx_sequence_number | tx_sequence_number | - -### Table `tx_senders` - -| Index name | Keys | Condition | -| ----------------------------------- | ------------------ | --------- | -| tx_senders_tx_sequence_number_index | tx_sequence_number | ASC | +| Index name | Keys | Condition | +| ----------------------------------------- | ------------------------------------- | --------- | +| tx_input_objects_tx_sequence_number_index | tx_sequence_number | | +| tx_input_objects_sender | sender, object_id, tx_sequence_number | | ### Tables `tx_recipients` -| Index name | Keys | Condition | -| -------------------------------------- | ------------------ | --------- | -| tx_recipients_tx_sequence_number_index | tx_sequence_number | ASC | +| Index name | Keys | Condition | +| -------------------- | ------------------------------------- | --------- | +| tx_recipients_sender | sender, recipient, tx_sequence_number | | ### Table `tx_count_metrics` @@ -93,6 +130,37 @@ The Indexer pulls checkpoint data from the full node and populates the tables sh | tx_count_metrics_epoch | epoch | | tx_count_metrics_timestamp_ms | timestamp_ms | +### Table `tx_changed_objects` + +| Index name | Keys | +| ------------------------------------------- | ------------------------------------- | +| tx_changed_objects_tx_sequence_number_index | tx_sequence_number | +| tx_changed_objects_sender | sender, object_id, tx_sequence_number | + +### Table `tx_calls_pkg` + +| Index name | Keys | +| ------------------- | ----------------------------------- | +| tx_calls_pkg_sender | sender, package, tx_sequence_number | + +### Table `tx_calls_mod` + +| Index name | Keys | +| ------------------- | ------------------------------------------- | +| tx_calls_mod_sender | sender, package, module, tx_sequence_number | + +### Table `tx_calls_fun` + +| Index name | Keys | +| ------------------- | ------------------------------------------------- | +| tx_calls_fun_sender | sender, package, module, func, tx_sequence_number | + +### Table `tx_digests` + +| Index name | Keys | +| ----------------------------- | ------------------ | +| tx_digests_tx_sequence_number | tx_sequence_number | + ### Table `move_calls` | Index name | Keys | @@ -111,10 +179,29 @@ The Indexer pulls checkpoint data from the full node and populates the tables sh | ------------------------- | ----- | | address_metrics_epoch_idx | epoch | +### Table `packages` + +| Index name | Keys | +| ---------------------- | -------------------------------------------------------- | +| packages_cp_id_version | checkpoint_sequence_number, original_id, package_version | +| packages_id_version_cp | original_id, package_version, checkpoint_sequence_number | + ## Partitions -### Tables `transactions`, `objects_history` +### Table `objects_history` | Keys | Condition | | -------------------------- | --------------------------------- | | checkpoint_sequence_number | FOR VALUES FROM (0) TO (MAXVALUE) | + +### Table `objects_version` + +| Keys | Condition | +| --------- | ------------------------------------------------------------- | +| object_id | custom function, create a partition for each first byte value | + +### Tables `transactions` & `events` + +| Keys | Condition | +| ------------------ | --------------------------------- | +| tx_sequence_number | FOR VALUES FROM (0) TO (MAXVALUE) | diff --git a/crates/iota-indexer/src/indexer_reader.rs b/crates/iota-indexer/src/indexer_reader.rs index 94fa737b6d6..3915588184f 100644 --- a/crates/iota-indexer/src/indexer_reader.rs +++ b/crates/iota-indexer/src/indexer_reader.rs @@ -63,7 +63,7 @@ use crate::{ }, schema::{ address_metrics, checkpoints, display, epochs, events, move_call_metrics, objects, - objects_snapshot, transactions, + objects_snapshot, packages, transactions, }, store::{diesel_macro::*, package_resolver::IndexerStorePackageResolver}, types::{IndexerResult, OwnerType}, @@ -1509,10 +1509,17 @@ impl IndexerReader { } pub fn get_latest_network_metrics(&self) -> IndexerResult { - let metrics = run_query!(&self.pool, |conn| { + let mut metrics = run_query!(&self.pool, |conn| { diesel::sql_query("SELECT * FROM network_metrics;") .get_result::(conn) })?; + if metrics.total_packages == -1 { + // this implies that the estimate is not available in the db + // so we fallback to the more expensive count query + metrics.total_packages = run_query!(&self.pool, |conn| { + packages::dsl::packages.count().get_result::(conn) + })?; + } Ok(metrics.into()) } diff --git a/crates/iota-protocol-config/src/lib.rs b/crates/iota-protocol-config/src/lib.rs index 3ca28d34e66..d13df4dfaab 100644 --- a/crates/iota-protocol-config/src/lib.rs +++ b/crates/iota-protocol-config/src/lib.rs @@ -2,9 +2,9 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::sync::{ - RwLock, - atomic::{AtomicBool, Ordering}, +use std::{ + cell::RefCell, + sync::atomic::{AtomicBool, Ordering}, }; use clap::*; @@ -1083,14 +1083,16 @@ impl ProtocolConfig { let mut ret = Self::get_for_version_impl(version, chain); ret.version = version; - if let Some(override_fn) = &*CONFIG_OVERRIDE.read().unwrap() { - warn!( - "overriding ProtocolConfig settings with custom settings (you should not see this log outside of tests)" - ); - override_fn(version, ret) - } else { - ret - } + CONFIG_OVERRIDE.with(|ovr| { + if let Some(override_fn) = &*ovr.borrow() { + warn!( + "overriding ProtocolConfig settings with custom settings (you should not see this log outside of tests)" + ); + override_fn(version, ret) + } else { + ret + } + }) } /// Get the value ProtocolConfig that are in effect during the given @@ -1708,10 +1710,12 @@ impl ProtocolConfig { pub fn apply_overrides_for_testing( override_fn: impl Fn(ProtocolVersion, Self) -> Self + Send + Sync + 'static, ) -> OverrideGuard { - let mut option = CONFIG_OVERRIDE.write().unwrap(); - assert!(option.is_none(), "config override already present"); - *option = Some(Box::new(override_fn)); - OverrideGuard + CONFIG_OVERRIDE.with(|ovr| { + let mut cur = ovr.borrow_mut(); + assert!(cur.is_none(), "config override already present"); + *cur = Some(Box::new(override_fn)); + OverrideGuard + }) } } @@ -1760,7 +1764,9 @@ impl ProtocolConfig { type OverrideFn = dyn Fn(ProtocolVersion, ProtocolConfig) -> ProtocolConfig + Send + Sync; -static CONFIG_OVERRIDE: RwLock>> = RwLock::new(None); +thread_local! { + static CONFIG_OVERRIDE: RefCell>> = const { RefCell::new(None) }; +} #[must_use] pub struct OverrideGuard; @@ -1768,7 +1774,9 @@ pub struct OverrideGuard; impl Drop for OverrideGuard { fn drop(&mut self) { info!("restoring override fn"); - *CONFIG_OVERRIDE.write().unwrap() = None; + CONFIG_OVERRIDE.with(|ovr| { + *ovr.borrow_mut() = None; + }); } } diff --git a/dashboards/README.md b/dashboards/README.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/docs/content/developer/advanced/introducing-move-2024.mdx b/docs/content/developer/advanced/introducing-move-2024.mdx new file mode 100644 index 00000000000..99d53feedcb --- /dev/null +++ b/docs/content/developer/advanced/introducing-move-2024.mdx @@ -0,0 +1,470 @@ +--- +title: Introducing Move 2024 +description: New features are becoming available to Move in 2024. These features are opt-in, so existing code will continue to function as expected. If you want to use these features in code you've already written, however, there are some steps you must take and breaking changes to be aware of to migrate to Move 2024. +--- + +IOTA launches with the "Move 2024.beta" edition, bringing enhanced features to improve both the writing and readability of Move code. These updates refine the source language without impacting the on-chain binary representation, allowing users to enjoy a smoother experience with the latest language improvements. + +The primary goal of the "Move 2024.beta" edition is to make Move easier to write and, ideally, easier to read. The minimal breaking changes introduced in the source language are designed to better prepare Move for future advancements. + +This document highlights some new features to try out and shows how to use them in your existing modules. + +:::info + +Please, provide any feedback or report any issues you encounter via [GitHub](https://github.com/iotaledger/iota/issues/new/choose) or [Discord](https://discord.iota.org/). + +::: + +## New features + +:::info + +Below are examples of how to rewrite some legacy code in the Move 2024 edition. +If you find legacy code in the Move examples and would like to rewrite it based on Move 2024, refer to these examples. + +::: + + +### Method Syntax + +You can now call certain function methods using the `.` syntax. For example, the following call: + +```move +vector::push_back(&mut v, coin::value(&c)); +``` + +Can now be written as: + +```move +v.push_back(c.value()); +``` + +Where the receiver of the method (`v` and `c` in this example) is automatically borrowed if necessary (as `&mut v` and `&c` respectively). + +You can call any function defined in the same module as the receiver's type as a method if it takes the receiver as its first argument. + +For functions defined outside the module, you can declare methods using `public use fun` and `use fun`. + +### Index Syntax + +With method syntax, you can annotate certain functions as being `#[syntax(index)]` methods. You then call these methods using `v[i]`-style calls. + +For example, + +```move +*&mut v[i] = v[j]; +``` + +resolves to + +```move +*vector::borrow_mut(&mut v, i) = *vector::borrow(&v, j); +``` + +### Macro Functions + +Higher-order functions (such as `map,` `filter,` `fold,` `for_each,` etc.) are useful in many languages for concisely transforming collections. Move does not have lambdas, closures, or function pointers, making defining these operations impossible. +Macro functions will allow for Move to mimic these sorts of operations without supporting the behavior at runtime. The body of the macro mimicking the "higher-order function" will be inlined at each call site. And the call site can provide a "lambda" that will be substituted as the macro is expanded. For example: + +```move +let v2 = v.map!(|x| x + 1); +``` + +Or + +```move +v.for_each!(|x| foo(x)); +``` + +The "lambdas" additionally will support control flow through break and return. + +### Enums + +Enumerations allow you to define a single type that may hold multiple different shapes of data. Unlike structs, which always have the same fields, enums can have different fields depending on the variant of the enum. For example, in `enum Option { None, Some(T) }`, the variant None has no fields, and the variant Some has a single field of type `T`. +Move allows destructuring enums using match expressions. Some examples of enums in Move are the following: + +```move +public enum Color { + RGB { red: u8, green: u8, blue: u8 }, + HSL { hue: u16, saturation: u8, lightness: u8 }, + Hex(u32) +} + +public enum Option { + None, + Some(T), +} + +public fun is_rgb_color(color: &Color): bool { + match (color) { + Color::RGB { red: _, green: _, blue: _ } => true, + _ => false, + } +} + +const EOptionIsNone: u64 = 0; +public fun unwrap_some(option: Option): T { + match (option) { + Option::Some(x) => x, + Option::None => abort EOptionIsNone, + } +} +``` + +Move will add support for basic high-level enums that have similar visibility rules to structs in Move today; the enumeration type is publicly visible, just like struct types, but the variants of the enumeration are not public, much like fields. However, we plan to add public variants in the future. Similarly, enumerations cannot be recursive at release, but we plan to support this in the future. + +### public(package) + +`friend` declarations, and the associated `public(friend)` visibility modifiers, are deprecated. In their place is the `public(package)` visibility modifier, which allows calling functions only within the same package where they are defined. + +### Positional Fields + +You can now define `struct`s with positional fields, which are accessed by zero-based index. For example, + +```move +public struct Pair(u64, u64) has copy, drop, store; +``` + +then to access each field, + +```move +public fun sum(p: &Pair): u64 { + p.0 + p.1 +} +``` + +And as this example shows, you can now declare abilities after the struct field list. + +### Postfix `has` Ability Declarations + +With positional fields, it is a bit awkward to read `has` declarations in the middle of a declaration. As an alternative, `has` can now be written after the fields. For example, both will be valid: + +```move +struct Wrapper1 has copy, drop, store (u64) +``` + +```move +struct Wrapper2(u64) has copy, drop, store; +``` + +### Nested `use` and Standard Library Defaults + +You can now nest `use` aliases for more conciseness. + +```move +use iota::{balance, coin::{Self, Coin}}; +``` + +Additionally, the following `use` declarations are now automatically included in every module: + +```move +use std::vector; +use std::option::{Self, Option}; +use iota::object::{Self, ID, UID}; +use iota::transfer; +use iota::tx_context::{Self, TxContext}; +``` + +### Automatic Referencing in Equality + +Equality operations, `==` and `!=`, now automatically borrow if one side is a reference and the other is not. For example, + +```move +fun check(x: u64, r: &u64): bool { + x == r +} +``` + +is equivalent to + +```move +fun check(x: u64, r: &u64): bool { + &x == r +} +``` + +This automatic borrowing can occur on either side of `==` and `!=`. + +### Loop Labels + +When nesting loops, it can be convenient to break to the outer loop. For example, + +```move +let mut i = 0; +let mut j = 0; +let mut terminate_loop = false; +while (i < 10) { + while (j < 10) { + if (haystack(i, j) == needle) { + terminate_loop = true; + break; + }; + j = j + 1; + }; + if (terminate_loop) break; + i = i + 1; +} +``` + +Now, you can directly name the outer loop (`outer` in this case) and break it all at once: + +```move +let mut i = 0; +let mut j = 0; +'outer: while (i < 10) { + while (j < 10) { + if (haystack(i, j) == needle) break'outer; + j = j + 1; + }; + i = i + 1; +} +``` +### Type Inference Holes `_` On Type Annotations + +With type directed programming, often you need to annotate a variable declaration or provide type arguments. But, sometimes you really only need to annotate on specific type, but the other types can be inferred. `_` will be added to allow that type to still be inferred, even when other parts of the type are annotated. For example + +```move +dynamic_field::borrow_mut>(&mut id, owner) +``` + +could be rewritten as + +```move +dynamic_field::borrow_mut<_, Coin>(&mut id, owner) +``` + +where the `_` would be inferred as `address` + + +### `break` With Value + +It's now possible to `break` with a value from a `loop`. For example, + +```move +let mut i = 0; +let x: u64 = loop { + if (v[i] > 10) break i; + i = i + 1; +}; +``` + +You can achieve this with labels, as well. For example, + +```move +let mut i = 0; +let mut j = 0; +let item = 'outer: loop { + while (j < 10) { + let item = haystack(i, j); + if (item == needle) break'outer option::some(item); + j = j + 1; + }; + i = i + 1; + if (i == 10) break option::none(); +}; +``` + +### Named Blocks With Enhanced Control Flow Operations + +Move 2024 supports naming `loop`, `while`, and normal blocks, allowing for more-complex control +flow. + +Previous code with nested `while` loops (such as this simplified excerpt from deepbook) would need to +set a flag to break both loops: + +```move +let mut terminate_loop = false; + +while (...loop_condition...) { + while (...inner_condition...) { + ... + if (...break_condition...) { + terminate_loop = true; + } + ... + if (terminate_loop) { + break; + } + } + if (terminate_loop) { + break; + } +} +``` + +Now, you can directly name the outer loop and break it all at once: + +```move +let mut terminate_loop = false; + +while (...loop_condition...) 'outer: { + while (...inner_condition...) { + ... + if (...break_condition...) { + terminate_loop = true; + } + ... + if (terminate_loop) { + break 'outer; + } + } +} +``` + +This will immediately break to the outer loop, allowing more precise control flow when you'd like +to escape from loops. + +This feature also works with normal loop forms, including breaks with values: + +```move +let y = loop 'outer: { + let _x = loop 'inner: { + if (true) { + break 'outer 10; + } else { + break 'inner 20 + } + }; +}; +``` + +In this toy example, `y` will take on the value `10` because the first `break` will break the +'outer` loop with that value. + +Finally, this feature can be applied to normal blocks in Move, but instead utilizes the `return` +keyword. This can be useful when sequencing a block of code that may need early returns with values. + +```move +public fun auth_user(auth_one: EasyAuth, auth_two: AuthTwo): u64 { + let auth_token = 'auth: { + let maybe_auth_token = try_auth(auth_one); + if (valid_auth(maybe_auth_token)) { + return 'auth unpack_auth(maybe_auth_token); + } + // ... more complicated code involving auth_two + }; + // ... code using the auth_token +} +``` + +While we do not expect programmers to use named blocks with return in everyday cases, we anticipate +that they will ease the development and usage of macros significantly. + + +## Breaking Changes + +### Datatype Visibility Requirements + +Currently, all structs in Move are, by convention, public: any other module or package can import them and refer to them by type. To make this clearer, Move 2024 requires that all structs be declared with the `public` keyword. For example: + +```move +// legacy code +struct S { x: u64 } + +// Move 2024 code +public struct S { x: u64 } +``` + +Any non-public struct produces an error at this time, though the Move team is working on new visibility options for future releases. + +### Mutability Requirements + +Previously, all variables in Move were implicitly mutable. For example: + +```move +fun f(s: S, y: u64): u64 { + let a = 0; + let S { x } = s; + a = 1; + x = 10; + y = 5; + x + y +} +``` + +Now, you must declare mutable variables explicitly: + +```move +fun f(s: S, mut y: u64): u64 { + let mut a = 0; + let S { mut x } = 5; + a = 1; + x = 10; + y = 5; + x + y +} +``` + +The compiler now produces an error if you attempt to reassign or borrow a variable mutably without this explicit declaration. + +### Removing Friends and `public(friend)` + +Friends and the `public(friend)` visibilities were introduced early in Move's development, predating even the package system. As indicated in the [public(package)](#publicpackage) section, `public(package)` deprecates `public(friend)` in Move 2024. + +The following declaration now produces an error: + +```move +module pkg::m { + friend pkg::a; + public(friend) fun f() { ... } +} + +module pkg::a { + fun calls_f() { ... pkg::m::f() ... } +} +``` + +Instead, if you want your function to be visible only in the package, write: + +```move +module pkg::m { + public(package) fun f() { ... } +} + +module pkg::a { + // this now works directly + fun calls_f() { ... pkg::m::f() ... } +} +``` + +### New Keywords + +Looking toward the future, Move 2024 Beta adds the following keywords to the language: `enum`, `for`, `match`, `mut`, and `type`. Unfortunately, the compiler now produces parsing errors when it finds these in other positions. This is a necessary change as the language matures. + +### Revised Paths and Namespaces + +Move 2024 revises how paths and namespaces work compared to legacy Move, toward easing `enum` aliasing in the future. Consider the following snippet from a test annotation in the `iota_system` library: + +```move +use iota_system::iota_system; +... +#[expected_failure(abort_code = iota_system::validator_set::EInvalidCap)] +``` + +Legacy Move would always treat a three-part name as an address(`iota_system`), module(`validator_set`), and module member (`EInvalidCap`). Move 2024 respects scope for `use`, so `iota_system` in the attribute resolves to the module, producing a name resolution error overall. + +To avoid cases where this is the intended behavior, Move 2024 introduces a prefix operation for global qualification. To use, you can rewrite this annotation as: + +```move +use iota_system::iota_system; +... +#[expected_failure(abort_code = ::iota_system::validator_set::EInvalidCap)] + // ^ note `::` here +``` + + +## Move 2024 Editions + +The beta release of Move 2024 comes with some powerful new features in addition to the breaking changes described here. There are also more on the horizon, such as syntactic macros, enums with pattern matching, and other user-defined syntax extensions. + +#### `beta` Edition +- `beta` (specified via `edition = "2024.beta"`) is the recommended edition. It includes all the new + features mentioned above and all breaking changes. While there is the risk of breaking changes or + bugs in `beta`, you should feel comfortable using it in your projects. As new features are added + and tested, they will be included in the `beta` edition. The `beta` edition will end after _all_ + features for the year have been added and finalized. + + #### `alpha` Edition +- `alpha` (specified via `edition = "2024.alpha"`) will get new features and changes as they are + developed. Breaking changes to features in `alpha` should be expected. As such, take caution when + using`alpha` in your projects. diff --git a/docs/content/sidebars/developer.js b/docs/content/sidebars/developer.js index 62e73e36874..19bc95c46d0 100644 --- a/docs/content/sidebars/developer.js +++ b/docs/content/sidebars/developer.js @@ -312,6 +312,7 @@ const developer = [ id: 'developer/advanced', }, items: [ + 'developer/advanced/introducing-move-2024', 'developer/advanced/iota-repository', 'developer/advanced/custom-indexer', 'developer/advanced/onchain-randomness',