diff --git a/.github/actions/diffs/action.yml b/.github/actions/diffs/action.yml index 5e1d52b786a22..37f2ae16d5bfb 100644 --- a/.github/actions/diffs/action.yml +++ b/.github/actions/diffs/action.yml @@ -22,6 +22,9 @@ outputs: isMoveAutoFormatter: description: True when changes happened in MoveAutoFormatter code value: "${{ steps.diff.outputs.isMoveAutoFormatter }}" + isExamples: + description: True when changes happened in examples/ directory + value: "${{ steps.diff.outputs.isExamples }}" runs: using: composite @@ -76,3 +79,5 @@ runs: - 'sui-execution/**' isMoveAutoFormatter: - 'external-crates/move/crates/move-analyzer/prettier-plugin/**' + isExamples: + - 'examples/**' diff --git a/.github/actions/ts-e2e/action.yml b/.github/actions/ts-e2e/action.yml index dce0d3c2d1041..c22d5480b19cd 100644 --- a/.github/actions/ts-e2e/action.yml +++ b/.github/actions/ts-e2e/action.yml @@ -61,10 +61,10 @@ runs: shell: bash - name: Run TS SDK e2e tests - run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @mysten/sui' + run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @mysten/sui test:e2e' shell: bash - name: Run TS SDK GraphQL compatibility e2e tests - if: ${{ inputs.ref != "testnet" && inputs.ref != "devnet" }} + if: (!contains(fromJSON('["testnet", "devnet"]'), inputs.ref)) run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @mysten/graphql-transport test:e2e' shell: bash diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index e412da242d88f..e52914687448f 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -29,22 +29,3 @@ jobs: - name: Build run: pnpm build working-directory: ./docs/site - - name: Check for changes in the examples directory - id: check_changes - run: | - if git diff --name-only $GITHUB_EVENT_BEFORE $GITHUB_SHA | grep '^examples/'; then - echo "examplechanges=true" >> $GITHUB_ENV - else - echo "examplechanges=false" >> $GITHUB_ENV - fi - - name: Notify Slack if examples directory changed - if: env.examplechanges == 'true' && github.event.action == 'opened' - uses: slackapi/slack-github-action@v1.24.0 - with: - payload: | - { - "channel": "#tech-docs", - "text": "An open PR modifies files in the `examples/` directory. Review it: " - } - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3ecb8b0e70441..8ad586eed974b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,11 +13,27 @@ jobs: outputs: isDoc: ${{ steps.diff.outputs.isDoc }} isOldDoc: ${{ steps.diff.outputs.isOldDoc }} + isExamples: ${{ steps.diff.outputs.isExamples }} steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 - name: Detect Changes uses: './.github/actions/diffs' id: diff + + examples: + name: Notify Slack if examples directory changed + needs: diff + if: needs.diff.outputs.isExamples == 'true' + runs-on: [ubuntu-latest] + + steps: + - name: Notify Slack of Examples Changes + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: 'tech-docs' + slack-message: "An open PR modifies files in the `examples/` directory. Review it: " + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} spelling: name: Lint documentation diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15955d9c1449d..662316958c4a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ run-name: Attach Sui binaries to a ${{ inputs.sui_tag }} release on: release: - types: [ published, prereleased ] + types: created workflow_dispatch: inputs: sui_tag: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 655c6a4cf4a1d..7a8e6ee5fe02d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -126,9 +126,6 @@ jobs: - name: cargo test run: | cargo nextest run --profile ci -E '!package(sui-bridge)' - - name: cargo test (sui-graphql staging) - run: | - cargo nextest run --profile ci --features staging -E 'package(sui-graphql-rpc)' -E 'package(sui-graphql-e2e-tests)' # Ensure there are no uncommitted changes in the repo after running tests - run: scripts/changed-files.sh shell: bash @@ -159,6 +156,9 @@ jobs: uses: pierotofy/set-swap-space@master with: swap-size-gb: 256 + - name: cargo test (sui-graphql staging) + run: | + cargo nextest run --profile ci --features staging -E 'package(sui-graphql-rpc)' -E 'package(sui-graphql-e2e-tests)' - name: benchmark (smoke) run: | cargo run --package sui-benchmark --bin stress -- --log-path /tmp/stress.log --num-client-threads 10 --num-server-threads 24 --num-transfer-accounts 2 bench --target-qps 100 --num-workers 10 --transfer-object 50 --shared-counter 50 --run-duration 10s --stress-stat-collection diff --git a/Cargo.lock b/Cargo.lock index 67e3fec556ebd..2fc013cf64efc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1542,6 +1542,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core 0.4.3", + "axum-macros", "base64 0.21.7", "bytes", "futures-util", @@ -1632,6 +1633,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.87", + "quote 1.0.35", + "syn 2.0.79", +] + [[package]] name = "axum-server" version = "0.6.1" @@ -13248,6 +13261,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", + "tokio-stream", "tracing", "url", ] @@ -13258,6 +13272,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "axum 0.7.5", "backoff", "bcs", "bigdecimal", @@ -13271,18 +13286,15 @@ dependencies = [ "prometheus", "serde", "serde_yaml 0.8.26", - "sui-bridge", "sui-config", "sui-data-ingestion-core", "sui-indexer-builder", "sui-json-rpc-types", "sui-sdk", - "sui-test-transaction-builder", "sui-types", "tap", "telemetry-subscribers", "tempfile", - "test-cluster", "tokio", "tracing", ] @@ -13334,6 +13346,7 @@ dependencies = [ "sui-protocol-config", "sui-rest-api", "sui-sdk", + "sui-sdk-types", "sui-simulator", "sui-storage", "sui-swarm", @@ -13677,6 +13690,7 @@ dependencies = [ "ntest", "object_store", "prometheus", + "rand 0.8.5", "rayon", "regex", "serde", @@ -14488,6 +14502,7 @@ dependencies = [ "fastcrypto", "itertools 0.13.0", "mime", + "move-binary-format", "mysten-network", "openapiv3", "prometheus", @@ -14987,6 +15002,7 @@ name = "sui-tls" version = "0.0.0" dependencies = [ "anyhow", + "arc-swap", "axum 0.7.5", "axum-server", "ed25519 1.5.3", diff --git a/Cargo.toml b/Cargo.toml index 6921ede3d998d..9a642f285f463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -265,6 +265,7 @@ aws-sdk-s3 = "0.29.0" aws-smithy-http = "0.56" aws-smithy-runtime-api = "0.56" axum = { version = "0.7", default-features = false, features = [ + "macros", "tokio", "http1", "http2", diff --git a/apps/wallet/src/ui/app/pages/swap/index.tsx b/apps/wallet/src/ui/app/pages/swap/index.tsx index e22562e13ceb1..93396613d06ab 100644 --- a/apps/wallet/src/ui/app/pages/swap/index.tsx +++ b/apps/wallet/src/ui/app/pages/swap/index.tsx @@ -183,6 +183,7 @@ export function SwapPage() { amount: parsed.toString(), slippage: Number(allowedMaxSlippagePercentage), enabled: isFormValid && parsed > 0n && !!fromCoinType && !!toCoinType, + source: 'sui-wallet', }); const swapData = useMemo(() => { diff --git a/apps/wallet/src/ui/app/pages/swap/useSwapTransaction.ts b/apps/wallet/src/ui/app/pages/swap/useSwapTransaction.ts index 94326c21a9049..b3cdf46d97c8e 100644 --- a/apps/wallet/src/ui/app/pages/swap/useSwapTransaction.ts +++ b/apps/wallet/src/ui/app/pages/swap/useSwapTransaction.ts @@ -8,6 +8,7 @@ export type SwapRequest = { amount: string; fromType?: string; slippage: number; + source: string; sender?: string; toType?: string; }; diff --git a/consensus/config/tests/snapshots/parameters_test__parameters.snap b/consensus/config/tests/snapshots/parameters_test__parameters.snap index ff7422faefd10..56e46f1bb3009 100644 --- a/consensus/config/tests/snapshots/parameters_test__parameters.snap +++ b/consensus/config/tests/snapshots/parameters_test__parameters.snap @@ -31,3 +31,4 @@ tonic: connection_buffer_size: 33554432 excessive_message_size: 16777216 message_size_limit: 67108864 + diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index b40b65a894336..1b8b818b79721 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -21,7 +21,7 @@ use crate::{ core::{Core, CoreSignals}, core_thread::{ChannelCoreThreadDispatcher, CoreThreadHandle}, dag_state::DagState, - leader_schedule::{LeaderSchedule, LeaderSwapTable}, + leader_schedule::LeaderSchedule, leader_timeout::{LeaderTimeoutTask, LeaderTimeoutTaskHandle}, metrics::initialise_metrics, network::{ @@ -233,20 +233,10 @@ where let block_manager = BlockManager::new(context.clone(), dag_state.clone(), block_verifier.clone()); - let leader_schedule = if context - .protocol_config - .mysticeti_leader_scoring_and_schedule() - { - Arc::new(LeaderSchedule::from_store( - context.clone(), - dag_state.clone(), - )) - } else { - Arc::new(LeaderSchedule::new( - context.clone(), - LeaderSwapTable::default(), - )) - }; + let leader_schedule = Arc::new(LeaderSchedule::from_store( + context.clone(), + dag_state.clone(), + )); let commit_consumer_monitor = commit_consumer.monitor(); commit_consumer_monitor @@ -426,7 +416,7 @@ mod tests { use std::{collections::BTreeSet, sync::Arc, time::Duration}; use consensus_config::{local_committee_and_keys, Parameters}; - use mysten_metrics::monitored_mpsc::{unbounded_channel, UnboundedReceiver}; + use mysten_metrics::monitored_mpsc::UnboundedReceiver; use prometheus::Registry; use rstest::rstest; use sui_protocol_config::ProtocolConfig; @@ -457,8 +447,7 @@ mod tests { let protocol_keypair = keypairs[own_index].1.clone(); let network_keypair = keypairs[own_index].0.clone(); - let (sender, _receiver) = unbounded_channel("consensus_output"); - let commit_consumer = CommitConsumer::new(sender, 0); + let (commit_consumer, _, _) = CommitConsumer::new(0); let authority = ConsensusAuthority::start( network_type, @@ -735,8 +724,7 @@ mod tests { let protocol_keypair = keypairs[index].1.clone(); let network_keypair = keypairs[index].0.clone(); - let (sender, receiver) = unbounded_channel("consensus_output"); - let commit_consumer = CommitConsumer::new(sender, 0); + let (commit_consumer, commit_receiver, _) = CommitConsumer::new(0); let authority = ConsensusAuthority::start( network_type, @@ -753,6 +741,6 @@ mod tests { ) .await; - (authority, receiver) + (authority, commit_receiver) } } diff --git a/consensus/core/src/commit.rs b/consensus/core/src/commit.rs index d40ee0a5a4ced..d1fbed756b461 100644 --- a/consensus/core/src/commit.rs +++ b/consensus/core/src/commit.rs @@ -19,6 +19,7 @@ use crate::{ block::{BlockAPI, BlockRef, BlockTimestampMs, Round, Slot, VerifiedBlock}, leader_scoring::ReputationScores, storage::Store, + TransactionIndex, }; /// Index of a commit among all consensus commits. @@ -109,6 +110,7 @@ pub(crate) struct CommitV1 { leader: BlockRef, /// Refs to committed blocks, in the commit order. blocks: Vec, + // TODO(fastpath): record rejected transactions. } impl CommitAPI for CommitV1 { @@ -293,6 +295,8 @@ pub struct CommittedSubDag { pub leader: BlockRef, /// All the committed blocks that are part of this sub-dag pub blocks: Vec, + /// Indices of rejected transactions in each block. + pub rejected_transactions_by_block: Vec>, /// The timestamp of the commit, obtained from the timestamp of the leader block. pub timestamp_ms: BlockTimestampMs, /// The reference of the commit. @@ -309,13 +313,16 @@ impl CommittedSubDag { pub fn new( leader: BlockRef, blocks: Vec, + rejected_transactions_by_block: Vec>, timestamp_ms: BlockTimestampMs, commit_ref: CommitRef, reputation_scores_desc: Vec<(AuthorityIndex, u64)>, ) -> Self { + assert_eq!(blocks.len(), rejected_transactions_by_block.len()); Self { leader, blocks, + rejected_transactions_by_block, timestamp_ms, commit_ref, reputation_scores_desc, @@ -386,11 +393,14 @@ pub fn load_committed_subdag_from_store( commit_block }) .collect::>(); + // TODO(fastpath): recover rejected transaction indices from commit. + let rejected_transactions = vec![vec![]; blocks.len()]; let leader_block_idx = leader_block_idx.expect("Leader block must be in the sub-dag"); let leader_block_ref = blocks[leader_block_idx].reference(); CommittedSubDag::new( leader_block_ref, blocks, + rejected_transactions, commit.timestamp_ms(), commit.reference(), reputation_scores_desc, @@ -508,16 +518,6 @@ pub(crate) struct CommitInfo { pub(crate) reputation_scores: ReputationScores, } -impl CommitInfo { - // Returns a new CommitInfo. - pub(crate) fn new(committed_rounds: Vec, reputation_scores: ReputationScores) -> Self { - CommitInfo { - committed_rounds, - reputation_scores, - } - } -} - /// CommitRange stores a range of CommitIndex. The range contains the start (inclusive) /// and end (inclusive) commit indices and can be ordered for use as the key of a table. /// diff --git a/consensus/core/src/commit_consumer.rs b/consensus/core/src/commit_consumer.rs index 2d06208d03964..adee1c716def1 100644 --- a/consensus/core/src/commit_consumer.rs +++ b/consensus/core/src/commit_consumer.rs @@ -4,13 +4,20 @@ use std::sync::{Arc, RwLock}; use tokio::sync::watch; -use mysten_metrics::monitored_mpsc::UnboundedSender; +use mysten_metrics::monitored_mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use crate::{CommitIndex, CommittedSubDag}; +use crate::{CommitIndex, CommittedSubDag, TransactionIndex, VerifiedBlock}; +#[derive(Clone)] pub struct CommitConsumer { - // A channel to send the committed sub dags through - pub(crate) sender: UnboundedSender, + // A channel to output the committed sub dags. + pub(crate) commit_sender: UnboundedSender, + // A channel to output certified and rejected transactions by batches of blocks. + // Each tuple contains the block containing transactions, and indices of rejected transactions. + // In each block, transactions that are not rejected are considered certified. + // Batches of blocks are sent together, to improve efficiency. + #[allow(unused)] + pub(crate) transaction_sender: UnboundedSender)>>, // Index of the last commit that the consumer has processed. This is useful for // crash/recovery so mysticeti can replay the commits from the next index. // First commit in the replayed sequence will have index last_processed_commit_index + 1. @@ -22,15 +29,26 @@ pub struct CommitConsumer { impl CommitConsumer { pub fn new( - sender: UnboundedSender, last_processed_commit_index: CommitIndex, - ) -> Self { + ) -> ( + Self, + UnboundedReceiver, + UnboundedReceiver)>>, + ) { + let (commit_sender, commit_receiver) = unbounded_channel("consensus_output"); + let (transaction_sender, transaction_receiver) = unbounded_channel("consensus_certified"); + let monitor = Arc::new(CommitConsumerMonitor::new(last_processed_commit_index)); - Self { - sender, - last_processed_commit_index, - monitor, - } + ( + Self { + commit_sender, + transaction_sender, + last_processed_commit_index, + monitor, + }, + commit_receiver, + transaction_receiver, + ) } pub fn monitor(&self) -> Arc { diff --git a/consensus/core/src/commit_observer.rs b/consensus/core/src/commit_observer.rs index a25113de387fb..9196235c3b934 100644 --- a/consensus/core/src/commit_observer.rs +++ b/consensus/core/src/commit_observer.rs @@ -55,7 +55,7 @@ impl CommitObserver { let mut observer = Self { context, commit_interpreter: Linearizer::new(dag_state.clone(), leader_schedule.clone()), - sender: commit_consumer.sender, + sender: commit_consumer.commit_sender, store, leader_schedule, }; @@ -207,7 +207,7 @@ impl CommitObserver { #[cfg(test)] mod tests { - use mysten_metrics::monitored_mpsc::{unbounded_channel, UnboundedReceiver}; + use mysten_metrics::monitored_mpsc::UnboundedReceiver; use parking_lot::RwLock; use super::*; @@ -227,7 +227,8 @@ mod tests { mem_store.clone(), ))); let last_processed_commit_index = 0; - let (sender, mut receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, mut commit_receiver, _transaction_receiver) = + CommitConsumer::new(last_processed_commit_index); let leader_schedule = Arc::new(LeaderSchedule::from_store( context.clone(), @@ -236,7 +237,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender, last_processed_commit_index), + commit_consumer, dag_state.clone(), mem_store.clone(), leader_schedule, @@ -289,7 +290,7 @@ mod tests { // Check commits sent over consensus output channel is accurate let mut processed_subdag_index = 0; - while let Ok(subdag) = receiver.try_recv() { + while let Ok(subdag) = commit_receiver.try_recv() { assert_eq!(subdag, commits[processed_subdag_index]); assert_eq!(subdag.reputation_scores_desc, vec![]); processed_subdag_index = subdag.commit_ref.index as usize; @@ -299,7 +300,7 @@ mod tests { } assert_eq!(processed_subdag_index, leaders.len()); - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); // Check commits have been persisted to storage let last_commit = mem_store.read_last_commit().unwrap().unwrap(); @@ -326,8 +327,8 @@ mod tests { mem_store.clone(), ))); let last_processed_commit_index = 0; - let (sender, mut receiver) = unbounded_channel("consensus_output"); - + let (commit_consumer, mut commit_receiver, _transaction_receiver) = + CommitConsumer::new(last_processed_commit_index); let leader_schedule = Arc::new(LeaderSchedule::from_store( context.clone(), dag_state.clone(), @@ -335,7 +336,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), last_processed_commit_index), + commit_consumer, dag_state.clone(), mem_store.clone(), leader_schedule.clone(), @@ -370,7 +371,7 @@ mod tests { // Check commits sent over consensus output channel is accurate let mut processed_subdag_index = 0; - while let Ok(subdag) = receiver.try_recv() { + while let Ok(subdag) = commit_receiver.try_recv() { tracing::info!("Processed {subdag}"); assert_eq!(subdag, commits[processed_subdag_index]); assert_eq!(subdag.reputation_scores_desc, vec![]); @@ -381,7 +382,7 @@ mod tests { } assert_eq!(processed_subdag_index, expected_last_processed_index); - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); // Check last stored commit is correct let last_commit = mem_store.read_last_commit().unwrap().unwrap(); @@ -406,7 +407,7 @@ mod tests { ); let expected_last_sent_index = num_rounds as usize; - while let Ok(subdag) = receiver.try_recv() { + while let Ok(subdag) = commit_receiver.try_recv() { tracing::info!("{subdag} was sent but not processed by consumer"); assert_eq!(subdag, commits[processed_subdag_index]); assert_eq!(subdag.reputation_scores_desc, vec![]); @@ -417,7 +418,7 @@ mod tests { } assert_eq!(processed_subdag_index, expected_last_sent_index); - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); // Check last stored commit is correct. We should persist the last commit // that was sent over the channel regardless of how the consumer handled @@ -427,9 +428,11 @@ mod tests { // Re-create commit observer starting from index 2 which represents the // last processed index from the consumer over consensus output channel + let (commit_consumer, mut commit_receiver, _transaction_receiver) = + CommitConsumer::new(expected_last_processed_index as CommitIndex); let _observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender, expected_last_processed_index as CommitIndex), + commit_consumer, dag_state.clone(), mem_store.clone(), leader_schedule, @@ -438,7 +441,7 @@ mod tests { // Check commits sent over consensus output channel is accurate starting // from last processed index of 2 and finishing at last sent index of 3. processed_subdag_index = expected_last_processed_index; - while let Ok(subdag) = receiver.try_recv() { + while let Ok(subdag) = commit_receiver.try_recv() { tracing::info!("Processed {subdag} on resubmission"); assert_eq!(subdag, commits[processed_subdag_index]); assert_eq!(subdag.reputation_scores_desc, vec![]); @@ -449,7 +452,7 @@ mod tests { } assert_eq!(processed_subdag_index, expected_last_sent_index); - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); } #[tokio::test] @@ -463,7 +466,8 @@ mod tests { mem_store.clone(), ))); let last_processed_commit_index = 0; - let (sender, mut receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, mut commit_receiver, _transaction_receiver) = + CommitConsumer::new(last_processed_commit_index); let leader_schedule = Arc::new(LeaderSchedule::from_store( context.clone(), @@ -472,7 +476,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), last_processed_commit_index), + commit_consumer, dag_state.clone(), mem_store.clone(), leader_schedule.clone(), @@ -499,7 +503,7 @@ mod tests { // Check commits sent over consensus output channel is accurate let mut processed_subdag_index = 0; - while let Ok(subdag) = receiver.try_recv() { + while let Ok(subdag) = commit_receiver.try_recv() { tracing::info!("Processed {subdag}"); assert_eq!(subdag, commits[processed_subdag_index]); assert_eq!(subdag.reputation_scores_desc, vec![]); @@ -510,7 +514,7 @@ mod tests { } assert_eq!(processed_subdag_index, expected_last_processed_index); - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); // Check last stored commit is correct let last_commit = mem_store.read_last_commit().unwrap().unwrap(); @@ -521,9 +525,11 @@ mod tests { // Re-create commit observer starting from index 3 which represents the // last processed index from the consumer over consensus output channel + let (commit_consumer, mut commit_receiver, _transaction_receiver) = + CommitConsumer::new(expected_last_processed_index as CommitIndex); let _observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender, expected_last_processed_index as CommitIndex), + commit_consumer, dag_state.clone(), mem_store.clone(), leader_schedule, @@ -531,7 +537,7 @@ mod tests { // No commits should be resubmitted as consensus store's last commit index // is equal to last processed index by consumer - verify_channel_empty(&mut receiver); + verify_channel_empty(&mut commit_receiver); } /// After receiving all expected subdags, ensure channel is empty diff --git a/consensus/core/src/commit_syncer.rs b/consensus/core/src/commit_syncer.rs index c4f4b09c6e49b..44a38d595d148 100644 --- a/consensus/core/src/commit_syncer.rs +++ b/consensus/core/src/commit_syncer.rs @@ -291,7 +291,8 @@ impl CommitSyncer { } debug!( - "Fetched certified blocks: {}", + "Fetched certified blocks for commit range {:?}: {}", + fetched_commit_range, blocks.iter().map(|b| b.reference().to_string()).join(","), ); // If core thread cannot handle the incoming blocks, it is ok to block here. @@ -301,7 +302,10 @@ impl CommitSyncer { match self.inner.core_thread_dispatcher.add_blocks(blocks).await { Ok(missing) => { if !missing.is_empty() { - warn!("Fetched blocks have missing ancestors: {:?}", missing); + warn!( + "Fetched blocks have missing ancestors: {:?} for commit range {:?}", + missing, fetched_commit_range + ); } } Err(e) => { diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index cef5242c2a525..ab9569243cef8 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -8,7 +8,7 @@ use consensus_config::{local_committee_and_keys, Stake}; use consensus_config::{AuthorityIndex, ProtocolKeyPair}; use itertools::Itertools as _; #[cfg(test)] -use mysten_metrics::monitored_mpsc::{unbounded_channel, UnboundedReceiver}; +use mysten_metrics::monitored_mpsc::UnboundedReceiver; use mysten_metrics::monitored_scope; use parking_lot::RwLock; use sui_macros::fail_point; @@ -506,137 +506,106 @@ impl Core { .with_label_values(&["Core::try_commit"]) .start_timer(); - if !self - .context - .protocol_config - .mysticeti_leader_scoring_and_schedule() - { - let decided_leaders = self.committer.try_decide(self.last_decided_leader); - if let Some(last) = decided_leaders.last() { - self.last_decided_leader = last.slot(); - self.context - .metrics - .node_metrics - .last_decided_leader_round - .set(self.last_decided_leader.round as i64); - } - - let committed_leaders = decided_leaders - .into_iter() - .filter_map(|leader| leader.into_committed_block()) - .collect::>(); - if !committed_leaders.is_empty() { - debug!( - "Committing leaders: {}", - committed_leaders - .iter() - .map(|b| b.reference().to_string()) - .join(",") - ); - } - self.commit_observer.handle_commit(committed_leaders) - } else { - let mut committed_subdags = Vec::new(); - // TODO: Add optimization to abort early without quorum for a round. - loop { - // LeaderSchedule has a limit to how many sequenced leaders can be committed - // before a change is triggered. Calling into leader schedule will get you - // how many commits till next leader change. We will loop back and recalculate - // any discarded leaders with the new schedule. - let mut commits_until_update = self - .leader_schedule - .commits_until_leader_schedule_update(self.dag_state.clone()); - if commits_until_update == 0 { - let last_commit_index = self.dag_state.read().last_commit_index(); - tracing::info!( - "Leader schedule change triggered at commit index {last_commit_index}" - ); - if self - .context - .protocol_config - .consensus_distributed_vote_scoring_strategy() - { - self.leader_schedule - .update_leader_schedule_v2(&self.dag_state); - } else { - self.leader_schedule - .update_leader_schedule_v1(&self.dag_state); - } - commits_until_update = self - .leader_schedule - .commits_until_leader_schedule_update(self.dag_state.clone()); - - fail_point!("consensus-after-leader-schedule-change"); - } - assert!(commits_until_update > 0); - - // TODO: limit commits by commits_until_update, which may be needed when leader schedule length - // is reduced. - let decided_leaders = self.committer.try_decide(self.last_decided_leader); - - let Some(last_decided) = decided_leaders.last().cloned() else { - break; - }; - tracing::debug!("Decided {} leaders and {commits_until_update} commits can be made before next leader schedule change", decided_leaders.len()); - - let mut sequenced_leaders = decided_leaders - .into_iter() - .filter_map(|leader| leader.into_committed_block()) - .collect::>(); - - // If the sequenced leaders are truncated to fit the leader schedule, use the last sequenced leader - // as the last decided leader. Otherwise, use the last decided leader from try_commit(). - let sequenced_leaders = if sequenced_leaders.len() >= commits_until_update { - let _ = sequenced_leaders.split_off(commits_until_update); - self.last_decided_leader = sequenced_leaders.last().unwrap().slot(); - sequenced_leaders - } else { - self.last_decided_leader = last_decided.slot(); - sequenced_leaders - }; - - self.context - .metrics - .node_metrics - .last_decided_leader_round - .set(self.last_decided_leader.round as i64); - - if sequenced_leaders.is_empty() { - break; - } + let mut committed_subdags = Vec::new(); + // TODO: Add optimization to abort early without quorum for a round. + loop { + // LeaderSchedule has a limit to how many sequenced leaders can be committed + // before a change is triggered. Calling into leader schedule will get you + // how many commits till next leader change. We will loop back and recalculate + // any discarded leaders with the new schedule. + let mut commits_until_update = self + .leader_schedule + .commits_until_leader_schedule_update(self.dag_state.clone()); + if commits_until_update == 0 { + let last_commit_index = self.dag_state.read().last_commit_index(); tracing::info!( - "Committing {} leaders: {}", - sequenced_leaders.len(), - sequenced_leaders - .iter() - .map(|b| b.reference().to_string()) - .join(",") + "Leader schedule change triggered at commit index {last_commit_index}" ); - - // TODO: refcount subdags - let subdags = self.commit_observer.handle_commit(sequenced_leaders)?; if self .context .protocol_config .consensus_distributed_vote_scoring_strategy() { - self.dag_state.write().add_scoring_subdags(subdags.clone()); + self.leader_schedule + .update_leader_schedule_v2(&self.dag_state); } else { - // TODO: Remove when DistributedVoteScoring is enabled. - self.dag_state - .write() - .add_unscored_committed_subdags(subdags.clone()); + self.leader_schedule + .update_leader_schedule_v1(&self.dag_state); } + commits_until_update = self + .leader_schedule + .commits_until_leader_schedule_update(self.dag_state.clone()); + + fail_point!("consensus-after-leader-schedule-change"); + } + assert!(commits_until_update > 0); - // Try to unsuspend blocks if gc_round has advanced. - self.block_manager - .try_unsuspend_blocks_for_latest_gc_round(); + // TODO: limit commits by commits_until_update, which may be needed when leader schedule length + // is reduced. + let decided_leaders = self.committer.try_decide(self.last_decided_leader); + + let Some(last_decided) = decided_leaders.last().cloned() else { + break; + }; + tracing::debug!("Decided {} leaders and {commits_until_update} commits can be made before next leader schedule change", decided_leaders.len()); + + let mut sequenced_leaders = decided_leaders + .into_iter() + .filter_map(|leader| leader.into_committed_block()) + .collect::>(); + + // If the sequenced leaders are truncated to fit the leader schedule, use the last sequenced leader + // as the last decided leader. Otherwise, use the last decided leader from try_commit(). + let sequenced_leaders = if sequenced_leaders.len() >= commits_until_update { + let _ = sequenced_leaders.split_off(commits_until_update); + self.last_decided_leader = sequenced_leaders.last().unwrap().slot(); + sequenced_leaders + } else { + self.last_decided_leader = last_decided.slot(); + sequenced_leaders + }; - committed_subdags.extend(subdags); + self.context + .metrics + .node_metrics + .last_decided_leader_round + .set(self.last_decided_leader.round as i64); + + if sequenced_leaders.is_empty() { + break; + } + tracing::info!( + "Committing {} leaders: {}", + sequenced_leaders.len(), + sequenced_leaders + .iter() + .map(|b| b.reference().to_string()) + .join(",") + ); + + // TODO: refcount subdags + let subdags = self.commit_observer.handle_commit(sequenced_leaders)?; + if self + .context + .protocol_config + .consensus_distributed_vote_scoring_strategy() + { + self.dag_state.write().add_scoring_subdags(subdags.clone()); + } else { + // TODO: Remove when DistributedVoteScoring is enabled. + self.dag_state + .write() + .add_unscored_committed_subdags(subdags.clone()); } - Ok(committed_subdags) + // Try to unsuspend blocks if gc_round has advanced. + self.block_manager + .try_unsuspend_blocks_for_latest_gc_round(); + + committed_subdags.extend(subdags); } + + Ok(committed_subdags) } pub(crate) fn get_missing_blocks(&self) -> BTreeSet { @@ -956,10 +925,10 @@ impl CoreTextFixture { // Need at least one subscriber to the block broadcast channel. let block_receiver = signal_receivers.block_broadcast_receiver(); - let (commit_sender, commit_receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(commit_sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -995,7 +964,6 @@ mod test { use std::{collections::BTreeSet, time::Duration}; use consensus_config::{AuthorityIndex, Parameters}; - use mysten_metrics::monitored_mpsc::unbounded_channel; use sui_protocol_config::ProtocolConfig; use tokio::time::sleep; @@ -1003,7 +971,7 @@ mod test { use crate::{ block::{genesis_blocks, TestBlock}, block_verifier::NoopBlockVerifier, - commit::{CommitAPI as _, CommitRange}, + commit::CommitAPI as _, leader_scoring::ReputationScores, storage::{mem_store::MemStore, Store, WriteBatch}, test_dag_builder::DagBuilder, @@ -1055,10 +1023,10 @@ mod test { dag_state.clone(), )); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1173,10 +1141,10 @@ mod test { dag_state.clone(), )); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1270,10 +1238,10 @@ mod test { dag_state.clone(), )); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1375,10 +1343,10 @@ mod test { // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1464,10 +1432,10 @@ mod test { // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1652,10 +1620,10 @@ mod test { // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1717,10 +1685,10 @@ mod test { // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -2008,127 +1976,6 @@ mod test { } } - #[tokio::test(flavor = "current_thread", start_paused = true)] - async fn test_no_leader_schedule_change() { - telemetry_subscribers::init_for_testing(); - let default_params = Parameters::default(); - - let (mut context, _) = Context::new_for_test(4); - context - .protocol_config - .set_mysticeti_leader_scoring_and_schedule_for_testing(false); - // create the cores and their signals for all the authorities - let mut cores = create_cores(context, vec![1, 1, 1, 1]); - - // Now iterate over a few rounds and ensure the corresponding signals are created while network advances - let mut last_round_blocks = Vec::new(); - for round in 1..=30 { - let mut this_round_blocks = Vec::new(); - - for core_fixture in &mut cores { - // Wait for min round delay to allow blocks to be proposed. - sleep(default_params.min_round_delay).await; - // add the blocks from last round - // this will trigger a block creation for the round and a signal should be emitted - core_fixture - .core - .add_blocks(last_round_blocks.clone()) - .unwrap(); - - // A "new round" signal should be received given that all the blocks of previous round have been processed - let new_round = receive( - Duration::from_secs(1), - core_fixture.signal_receivers.new_round_receiver(), - ) - .await; - assert_eq!(new_round, round); - - // Check that a new block has been proposed. - let block = tokio::time::timeout( - Duration::from_secs(1), - core_fixture.block_receiver.recv(), - ) - .await - .unwrap() - .unwrap(); - assert_eq!(block.round(), round); - assert_eq!(block.author(), core_fixture.core.context.own_index); - - // append the new block to this round blocks - this_round_blocks.push(core_fixture.core.last_proposed_block().clone()); - - let block = core_fixture.core.last_proposed_block(); - - // ensure that produced block is referring to the blocks of last_round - assert_eq!( - block.ancestors().len(), - core_fixture.core.context.committee.size() - ); - for ancestor in block.ancestors() { - if block.round() > 1 { - // don't bother with round 1 block which just contains the genesis blocks. - assert!( - last_round_blocks - .iter() - .any(|block| block.reference() == *ancestor), - "Reference from previous round should be added" - ); - } - } - } - - last_round_blocks = this_round_blocks; - } - - for core_fixture in cores { - // Check commits have been persisted to store - let last_commit = core_fixture - .store - .read_last_commit() - .unwrap() - .expect("last commit should be set"); - // There are 28 leader rounds with rounds completed up to and including - // round 29. Round 30 blocks will only include their own blocks, so the - // 28th leader will not be committed. - assert_eq!(last_commit.index(), 27); - let all_stored_commits = core_fixture - .store - .scan_commits((0..=CommitIndex::MAX).into()) - .unwrap(); - assert_eq!(all_stored_commits.len(), 27); - assert_eq!( - core_fixture - .core - .leader_schedule - .leader_swap_table - .read() - .bad_nodes - .len(), - 0 - ); - assert_eq!( - core_fixture - .core - .leader_schedule - .leader_swap_table - .read() - .good_nodes - .len(), - 0 - ); - let expected_reputation_scores = ReputationScores::new(CommitRange::default(), vec![]); - assert_eq!( - core_fixture - .core - .leader_schedule - .leader_swap_table - .read() - .reputation_scores, - expected_reputation_scores - ); - } - } - #[tokio::test(flavor = "current_thread", start_paused = true)] async fn test_commit_on_leader_schedule_change_boundary_without_multileader() { parameterized_test_commit_on_leader_schedule_change_boundary(Some(1)).await; diff --git a/consensus/core/src/core_thread.rs b/consensus/core/src/core_thread.rs index 63cc514d77e29..5b2a83748672e 100644 --- a/consensus/core/src/core_thread.rs +++ b/consensus/core/src/core_thread.rs @@ -390,7 +390,6 @@ impl CoreThreadDispatcher for MockCoreThreadDispatcher { #[cfg(test)] mod test { - use mysten_metrics::monitored_mpsc::unbounded_channel; use parking_lot::RwLock; use super::*; @@ -423,14 +422,14 @@ mod test { let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); let _block_receiver = signal_receivers.block_broadcast_receiver(); - let (sender, _receiver) = unbounded_channel("consensus_output"); + let (commit_consumer, _commit_receiver, _transaction_receiver) = CommitConsumer::new(0); let leader_schedule = Arc::new(LeaderSchedule::from_store( context.clone(), dag_state.clone(), )); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0), + commit_consumer, dag_state.clone(), store, leader_schedule.clone(), diff --git a/consensus/core/src/dag_state.rs b/consensus/core/src/dag_state.rs index a8adc047ae4c2..cfe7ba080dbcb 100644 --- a/consensus/core/src/dag_state.rs +++ b/consensus/core/src/dag_state.rs @@ -46,9 +46,9 @@ pub(crate) struct DagState { // Note: all uncommitted blocks are kept in memory. recent_blocks: BTreeMap, - // Contains block refs of recent_blocks. - // Each element in the Vec corresponds to the authority with the index. - recent_refs: Vec>, + // Indexes recent block refs by their authorities. + // Vec position corresponds to the authority index. + recent_refs_by_authority: Vec>, // Highest round of blocks accepted. highest_accepted_round: Round, @@ -132,17 +132,10 @@ impl DagState { last_committed_rounds[block_ref.author] = max(last_committed_rounds[block_ref.author], block_ref.round); } - if context - .protocol_config - .mysticeti_leader_scoring_and_schedule() - { - let committed_subdag = load_committed_subdag_from_store( - store.as_ref(), - commit.clone(), - vec![], - ); // We don't need to recover reputation scores for unscored_committed_subdags - unscored_committed_subdags.push(committed_subdag); - } + let committed_subdag = + load_committed_subdag_from_store(store.as_ref(), commit.clone(), vec![]); + // We don't need to recover reputation scores for unscored_committed_subdags + unscored_committed_subdags.push(committed_subdag); }); } @@ -163,7 +156,7 @@ impl DagState { context, genesis, recent_blocks: BTreeMap::new(), - recent_refs: vec![BTreeSet::new(); num_authorities], + recent_refs_by_authority: vec![BTreeSet::new(); num_authorities], highest_accepted_round: 0, last_commit, last_commit_round_advancement_time: None, @@ -245,7 +238,7 @@ impl DagState { fn update_block_metadata(&mut self, block: &VerifiedBlock) { let block_ref = block.reference(); self.recent_blocks.insert(block_ref, block.clone()); - self.recent_refs[block_ref.author].insert(block_ref); + self.recent_refs_by_authority[block_ref.author].insert(block_ref); self.highest_accepted_round = max(self.highest_accepted_round, block.round()); self.context .metrics @@ -253,7 +246,7 @@ impl DagState { .highest_accepted_round .set(self.highest_accepted_round as i64); - let highest_accepted_round_for_author = self.recent_refs[block_ref.author] + let highest_accepted_round_for_author = self.recent_refs_by_authority[block_ref.author] .last() .map(|block_ref| block_ref.round) .expect("There should be by now at least one block ref"); @@ -414,7 +407,7 @@ impl DagState { /// Retrieves the last block proposed for the specified `authority`. If no block is found in cache /// then the genesis block is returned as no other block has been received from that authority. pub(crate) fn get_last_block_for_authority(&self, authority: AuthorityIndex) -> VerifiedBlock { - if let Some(last) = self.recent_refs[authority].last() { + if let Some(last) = self.recent_refs_by_authority[authority].last() { return self .recent_blocks .get(last) @@ -442,7 +435,7 @@ impl DagState { start: Round, ) -> Vec { let mut blocks = vec![]; - for block_ref in self.recent_refs[authority].range(( + for block_ref in self.recent_refs_by_authority[authority].range(( Included(BlockRef::new(start, authority, BlockDigest::MIN)), Unbounded, )) { @@ -477,7 +470,7 @@ impl DagState { return blocks; } - for (authority_index, block_refs) in self.recent_refs.iter().enumerate() { + for (authority_index, block_refs) in self.recent_refs_by_authority.iter().enumerate() { let authority_index = self .context .committee @@ -527,7 +520,7 @@ impl DagState { ); } - let mut result = self.recent_refs[slot.authority].range(( + let mut result = self.recent_refs_by_authority[slot.authority].range(( Included(BlockRef::new(slot.round, slot.authority, BlockDigest::MIN)), Included(BlockRef::new(slot.round, slot.authority, BlockDigest::MAX)), )); @@ -541,7 +534,7 @@ impl DagState { let mut missing = Vec::new(); for (index, block_ref) in block_refs.into_iter().enumerate() { - let recent_refs = &self.recent_refs[block_ref.author]; + let recent_refs = &self.recent_refs_by_authority[block_ref.author]; if recent_refs.contains(&block_ref) || self.genesis.contains_key(&block_ref) { exist[index] = true; } else if recent_refs.is_empty() || recent_refs.last().unwrap().round < block_ref.round @@ -760,22 +753,7 @@ impl DagState { // Flush buffered data to storage. let blocks = std::mem::take(&mut self.blocks_to_write); let commits = std::mem::take(&mut self.commits_to_write); - let commit_info_to_write = if self - .context - .protocol_config - .mysticeti_leader_scoring_and_schedule() - { - std::mem::take(&mut self.commit_info_to_write) - } else if commits.is_empty() { - vec![] - } else { - let last_commit_ref = commits.last().as_ref().unwrap().reference(); - let commit_info = CommitInfo::new( - self.last_committed_rounds.clone(), - ReputationScores::default(), - ); - vec![(last_commit_ref, commit_info)] - }; + let commit_info_to_write = std::mem::take(&mut self.commit_info_to_write); if blocks.is_empty() && commits.is_empty() { return; @@ -804,7 +782,7 @@ impl DagState { // Clean up old cached data. After flushing, all cached blocks are guaranteed to be persisted. let mut total_recent_refs = 0; for (authority_refs, last_committed_round) in self - .recent_refs + .recent_refs_by_authority .iter_mut() .zip(self.last_committed_rounds.iter()) { diff --git a/consensus/core/src/leader_schedule.rs b/consensus/core/src/leader_schedule.rs index 756cc9361d86b..56c9813a0d836 100644 --- a/consensus/core/src/leader_schedule.rs +++ b/consensus/core/src/leader_schedule.rs @@ -566,116 +566,6 @@ mod tests { ); } - #[tokio::test] - async fn test_leader_schedule_from_store_with_no_scores() { - telemetry_subscribers::init_for_testing(); - let mut context = Context::new_for_test(4).0; - context - .protocol_config - .set_mysticeti_leader_scoring_and_schedule_for_testing(false); - let context = Arc::new(context); - let store = Arc::new(MemStore::new()); - - let leader_timestamp = context.clock.timestamp_utc_ms(); - let blocks = vec![ - VerifiedBlock::new_for_test( - TestBlock::new(10, 2) - .set_timestamp_ms(leader_timestamp) - .build(), - ), - VerifiedBlock::new_for_test(TestBlock::new(9, 0).build()), - VerifiedBlock::new_for_test(TestBlock::new(9, 2).build()), - VerifiedBlock::new_for_test(TestBlock::new(9, 3).build()), - ]; - - let leader = blocks[0].clone(); - let leader_ref = leader.reference(); - let last_commit_index = 10; - let last_commit = TrustedCommit::new_for_test( - last_commit_index, - CommitDigest::MIN, - leader_timestamp, - leader_ref, - blocks - .iter() - .map(|block| block.reference()) - .collect::>(), - ); - - // The CommitInfo for the first 10 commits are written to store. This is the - // info that LeaderSchedule will be recovered from - let committed_rounds = vec![9, 9, 10, 9]; - let commit_ref = CommitRef::new(10, CommitDigest::MIN); - let commit_info = CommitInfo { - reputation_scores: ReputationScores::default(), - committed_rounds, - }; - - store - .write( - WriteBatch::default() - .commit_info(vec![(commit_ref, commit_info)]) - .blocks(blocks) - .commits(vec![last_commit]), - ) - .unwrap(); - - // CommitIndex '11' will be written to store. This should result in the cached - // last_committed_rounds & unscored subdags in DagState to be updated with the - // latest commit information on recovery. - let leader_timestamp = context.clock.timestamp_utc_ms(); - let blocks = vec![ - VerifiedBlock::new_for_test( - TestBlock::new(11, 3) - .set_timestamp_ms(leader_timestamp) - .build(), - ), - VerifiedBlock::new_for_test(TestBlock::new(10, 0).build()), - VerifiedBlock::new_for_test(TestBlock::new(10, 1).build()), - VerifiedBlock::new_for_test(TestBlock::new(10, 3).build()), - ]; - - let leader = blocks[0].clone(); - let leader_ref = leader.reference(); - let last_commit_index = 11; - let expected_last_committed_rounds = vec![10, 10, 10, 11]; - let last_commit = TrustedCommit::new_for_test( - last_commit_index, - CommitDigest::MIN, - leader_timestamp, - leader_ref, - blocks - .iter() - .map(|block| block.reference()) - .collect::>(), - ); - store - .write( - WriteBatch::default() - .blocks(blocks) - .commits(vec![last_commit]), - ) - .unwrap(); - - let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store))); - - // Check that DagState recovery from stored CommitInfo worked correctly - assert_eq!( - expected_last_committed_rounds, - dag_state.read().last_committed_rounds() - ); - - // Leader Scoring & Schedule Change is disabled, unscored subdags should not be accumulated. - assert_eq!(0, dag_state.read().scoring_subdags_count()); - - let leader_schedule = LeaderSchedule::from_store(context.clone(), dag_state.clone()); - - // Check that LeaderSchedule recovery from stored CommitInfo worked correctly - let leader_swap_table = leader_schedule.leader_swap_table.read(); - assert_eq!(leader_swap_table.good_nodes.len(), 0); - assert_eq!(leader_swap_table.bad_nodes.len(), 0); - } - #[tokio::test] async fn test_leader_schedule_from_store() { telemetry_subscribers::init_for_testing(); @@ -888,6 +778,7 @@ mod tests { let unscored_subdags = vec![CommittedSubDag::new( BlockRef::new(1, AuthorityIndex::ZERO, BlockDigest::MIN), vec![], + vec![], context.clock.timestamp_utc_ms(), CommitRef::new(1, CommitDigest::MIN), vec![], @@ -969,6 +860,7 @@ mod tests { let leader_block = leader.unwrap(); let leader_ref = leader_block.reference(); let commit_index = 1; + let rejected_transactions = vec![vec![]; blocks.len()]; let last_commit = TrustedCommit::new_for_test( commit_index, @@ -984,6 +876,7 @@ mod tests { let unscored_subdags = vec![CommittedSubDag::new( leader_ref, blocks, + rejected_transactions, context.clock.timestamp_utc_ms(), last_commit.reference(), vec![], @@ -1195,119 +1088,6 @@ mod tests { } // TODO: Remove all tests below this when DistributedVoteScoring is enabled. - #[tokio::test] - async fn test_leader_schedule_from_store_with_no_scores_with_vote_scoring() { - telemetry_subscribers::init_for_testing(); - let mut context = Context::new_for_test(4).0; - context - .protocol_config - .set_consensus_distributed_vote_scoring_strategy_for_testing(false); - context - .protocol_config - .set_mysticeti_leader_scoring_and_schedule_for_testing(false); - let context = Arc::new(context); - let store = Arc::new(MemStore::new()); - - let leader_timestamp = context.clock.timestamp_utc_ms(); - let blocks = vec![ - VerifiedBlock::new_for_test( - TestBlock::new(10, 2) - .set_timestamp_ms(leader_timestamp) - .build(), - ), - VerifiedBlock::new_for_test(TestBlock::new(9, 0).build()), - VerifiedBlock::new_for_test(TestBlock::new(9, 2).build()), - VerifiedBlock::new_for_test(TestBlock::new(9, 3).build()), - ]; - - let leader = blocks[0].clone(); - let leader_ref = leader.reference(); - let last_commit_index = 10; - let last_commit = TrustedCommit::new_for_test( - last_commit_index, - CommitDigest::MIN, - leader_timestamp, - leader_ref, - blocks - .iter() - .map(|block| block.reference()) - .collect::>(), - ); - - // The CommitInfo for the first 10 commits are written to store. This is the - // info that LeaderSchedule will be recovered from - let committed_rounds = vec![9, 9, 10, 9]; - let commit_ref = CommitRef::new(10, CommitDigest::MIN); - let commit_info = CommitInfo { - reputation_scores: ReputationScores::default(), - committed_rounds, - }; - - store - .write( - WriteBatch::default() - .commit_info(vec![(commit_ref, commit_info)]) - .blocks(blocks) - .commits(vec![last_commit]), - ) - .unwrap(); - - // CommitIndex '11' will be written to store. This should result in the cached - // last_committed_rounds & unscored subdags in DagState to be updated with the - // latest commit information on recovery. - let leader_timestamp = context.clock.timestamp_utc_ms(); - let blocks = vec![ - VerifiedBlock::new_for_test( - TestBlock::new(11, 3) - .set_timestamp_ms(leader_timestamp) - .build(), - ), - VerifiedBlock::new_for_test(TestBlock::new(10, 0).build()), - VerifiedBlock::new_for_test(TestBlock::new(10, 1).build()), - VerifiedBlock::new_for_test(TestBlock::new(10, 3).build()), - ]; - - let leader = blocks[0].clone(); - let leader_ref = leader.reference(); - let last_commit_index = 11; - let expected_last_committed_rounds = vec![10, 10, 10, 11]; - let last_commit = TrustedCommit::new_for_test( - last_commit_index, - CommitDigest::MIN, - leader_timestamp, - leader_ref, - blocks - .iter() - .map(|block| block.reference()) - .collect::>(), - ); - store - .write( - WriteBatch::default() - .blocks(blocks) - .commits(vec![last_commit]), - ) - .unwrap(); - - let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store))); - - // Check that DagState recovery from stored CommitInfo worked correctly - assert_eq!( - expected_last_committed_rounds, - dag_state.read().last_committed_rounds() - ); - - // Leader Scoring & Schedule Change is disabled, unscored subdags should not be accumulated. - assert_eq!(0, dag_state.read().unscored_committed_subdags_count()); - - let leader_schedule = LeaderSchedule::from_store(context.clone(), dag_state.clone()); - - // Check that LeaderSchedule recovery from stored CommitInfo worked correctly - let leader_swap_table = leader_schedule.leader_swap_table.read(); - assert_eq!(leader_swap_table.good_nodes.len(), 0); - assert_eq!(leader_swap_table.bad_nodes.len(), 0); - } - #[tokio::test] async fn test_leader_schedule_from_store_with_vote_scoring() { telemetry_subscribers::init_for_testing(); @@ -1535,6 +1315,7 @@ mod tests { let unscored_subdags = vec![CommittedSubDag::new( BlockRef::new(1, AuthorityIndex::ZERO, BlockDigest::MIN), vec![], + vec![], context.clock.timestamp_utc_ms(), CommitRef::new(1, CommitDigest::MIN), vec![], @@ -1622,6 +1403,7 @@ mod tests { let leader_block = leader.unwrap(); let leader_ref = leader_block.reference(); let commit_index = 1; + let rejected_transactions = vec![vec![]; blocks.len()]; let last_commit = TrustedCommit::new_for_test( commit_index, @@ -1637,6 +1419,7 @@ mod tests { let unscored_subdags = vec![CommittedSubDag::new( leader_ref, blocks, + rejected_transactions, context.clock.timestamp_utc_ms(), last_commit.reference(), vec![], diff --git a/consensus/core/src/leader_scoring.rs b/consensus/core/src/leader_scoring.rs index c7b151607cd01..89803a4a273f3 100644 --- a/consensus/core/src/leader_scoring.rs +++ b/consensus/core/src/leader_scoring.rs @@ -692,6 +692,7 @@ mod tests { let unscored_subdags = vec![CommittedSubDag::new( BlockRef::new(1, AuthorityIndex::ZERO, BlockDigest::MIN), blocks, + vec![], context.clock.timestamp_utc_ms(), CommitRef::new(1, CommitDigest::MIN), vec![], diff --git a/consensus/core/src/linearizer.rs b/consensus/core/src/linearizer.rs index cc0254b288e35..f362d7ef2f7fc 100644 --- a/consensus/core/src/linearizer.rs +++ b/consensus/core/src/linearizer.rs @@ -107,6 +107,10 @@ impl Linearizer { // Sort the blocks of the sub-dag blocks sort_sub_dag_blocks(&mut to_commit); + // TODO(fastpath): determine rejected transactions from voting. + // Get rejected transactions. + let rejected_transactions = vec![vec![]; to_commit.len()]; + // Create the Commit. let commit = Commit::new( last_commit_index + 1, @@ -127,6 +131,7 @@ impl Linearizer { let sub_dag = CommittedSubDag::new( leader_block_ref, to_commit, + rejected_transactions, timestamp_ms, commit.reference(), reputation_scores_desc, diff --git a/consensus/core/src/network/tonic_tls.rs b/consensus/core/src/network/tonic_tls.rs index 88a40f88c4103..13377934e3b18 100644 --- a/consensus/core/src/network/tonic_tls.rs +++ b/consensus/core/src/network/tonic_tls.rs @@ -1,19 +1,22 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::BTreeSet; - +use crate::context::Context; use consensus_config::{AuthorityIndex, NetworkKeyPair}; -use fastcrypto::ed25519::Ed25519PublicKey; +use sui_tls::AllowPublicKeys; use tokio_rustls::rustls::{ClientConfig, ServerConfig}; -use crate::context::Context; - pub(crate) fn create_rustls_server_config( context: &Context, network_keypair: NetworkKeyPair, ) -> ServerConfig { - let allower = AllowedPublicKeys::new(context); + let allower = AllowPublicKeys::new( + context + .committee + .authorities() + .map(|(_i, a)| a.network_key.clone().into_inner()) + .collect(), + ); let verifier = sui_tls::ClientCertVerifier::new(allower, certificate_server_name(context)); // TODO: refactor to use key bytes let self_signed_cert = sui_tls::SelfSignedCertificate::new( @@ -56,30 +59,6 @@ pub(crate) fn create_rustls_client_config( tls_config } -// Checks if the public key from a TLS certificate belongs to one of the validators. -#[derive(Debug)] -struct AllowedPublicKeys { - // TODO: refactor to use key bytes - keys: BTreeSet, -} - -impl AllowedPublicKeys { - fn new(context: &Context) -> Self { - let keys = context - .committee - .authorities() - .map(|(_i, a)| a.network_key.clone().into_inner()) - .collect(); - Self { keys } - } -} - -impl sui_tls::Allower for AllowedPublicKeys { - fn allowed(&self, key: &Ed25519PublicKey) -> bool { - self.keys.contains(key) - } -} - fn certificate_server_name(context: &Context) -> String { format!("consensus_epoch_{}", context.committee.epoch()) } diff --git a/consensus/core/src/storage/rocksdb_store.rs b/consensus/core/src/storage/rocksdb_store.rs index edee2b3bb9779..956383b5002a9 100644 --- a/consensus/core/src/storage/rocksdb_store.rs +++ b/consensus/core/src/storage/rocksdb_store.rs @@ -54,10 +54,9 @@ impl RocksDBStore { ( Self::BLOCKS_CF, default_db_options() - .optimize_for_write_throughput() - // Blocks can get large and they don't need to be compacted. - // So keep them in rocksdb blobstore. - .optimize_for_large_values_no_scan(1 << 10) + .optimize_for_write_throughput_no_deletion() + // Using larger block is ok since there is not much point reads on the cf. + .set_block_options(512, 128 << 10) .options, ), (Self::DIGESTS_BY_AUTHORITIES_CF, cf_options.clone()), diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index 8913aa804e4d1..dbcf447be9b4e 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -52,6 +52,12 @@ const MAX_BLOCKS_PER_FETCH: usize = 32; const MAX_AUTHORITIES_TO_FETCH_PER_BLOCK: usize = 2; +/// The number of rounds above the highest accepted round that still willing to fetch missing blocks via the periodic +/// synchronizer. Any missing blocks of higher rounds are considered too far in the future to fetch. This property is taken into +/// account only when it's detected that the node has fallen behind on its commit compared to the rest of the network, otherwise +/// scheduler will attempt to fetch any missing block. +const SYNC_MISSING_BLOCK_ROUND_THRESHOLD: u32 = 50; + struct BlocksGuard { map: Arc, block_refs: BTreeSet, @@ -827,19 +833,7 @@ impl Synchronizer ConsensusResult<()> { - let (commit_lagging, last_commit_index, quorum_commit_index) = self.is_commit_lagging(); - if commit_lagging { - trace!("Scheduled synchronizer temporarily disabled as local commit is falling behind from quorum {last_commit_index} << {quorum_commit_index}"); - self.context - .metrics - .node_metrics - .fetch_blocks_scheduler_skipped - .with_label_values(&["commit_lagging"]) - .inc(); - return Ok(()); - } - - let missing_blocks = self + let mut missing_blocks = self .core_dispatcher .get_missing_blocks() .await @@ -859,6 +853,31 @@ impl Synchronizer>(); + + // If no missing blocks are within the acceptable thresholds to sync while we commit lag, then we disable the scheduler completely for this run. + if missing_blocks.is_empty() { + trace!("Scheduled synchronizer temporarily disabled as local commit is falling behind from quorum {last_commit_index} << {quorum_commit_index} and missing blocks are too far in the future."); + self.context + .metrics + .node_metrics + .fetch_blocks_scheduler_skipped + .with_label_values(&["commit_lagging"]) + .inc(); + return Ok(()); + } + } + self.fetch_blocks_scheduler_task .spawn(monitored_future!(async move { let _scope = monitored_scope("FetchMissingBlocksScheduler"); @@ -1036,8 +1055,11 @@ mod tests { use parking_lot::RwLock; use tokio::{sync::Mutex, time::sleep}; - use crate::commit::{CommitVote, TrustedCommit}; - use crate::{authority_service::COMMIT_LAG_MULTIPLIER, core_thread::MockCoreThreadDispatcher}; + use crate::{ + authority_service::COMMIT_LAG_MULTIPLIER, + core_thread::MockCoreThreadDispatcher, + synchronizer::{MAX_BLOCKS_PER_FETCH, SYNC_MISSING_BLOCK_ROUND_THRESHOLD}, + }; use crate::{ block::{BlockDigest, BlockRef, Round, TestBlock, VerifiedBlock}, block_verifier::NoopBlockVerifier, @@ -1054,6 +1076,10 @@ mod tests { }, CommitDigest, CommitIndex, }; + use crate::{ + commit::{CommitVote, TrustedCommit}, + BlockAPI, + }; type FetchRequestKey = (Vec, AuthorityIndex); type FetchRequestResponse = (Vec, Option); @@ -1444,7 +1470,8 @@ mod tests { } #[tokio::test(flavor = "current_thread", start_paused = true)] - async fn synchronizer_periodic_task_skip_when_commit_lagging() { + async fn synchronizer_periodic_task_when_commit_lagging_with_missing_blocks_in_acceptable_thresholds( + ) { // GIVEN let (context, _) = Context::new_for_test(4); let context = Arc::new(context); @@ -1455,8 +1482,96 @@ mod tests { let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store))); let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); - // AND stub some missing blocks - let expected_blocks = (0..10) + // AND stub some missing blocks. The highest accepted round is 0. Create some blocks that are below and above the threshold sync. + let expected_blocks = (0..SYNC_MISSING_BLOCK_ROUND_THRESHOLD * 2) + .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build())) + .collect::>(); + + let missing_blocks = expected_blocks + .iter() + .map(|block| block.reference()) + .collect::>(); + core_dispatcher.stub_missing_blocks(missing_blocks).await; + + // AND stub the requests for authority 1 & 2 + // Make the first authority timeout, so the second will be called. "We" are authority = 0, so + // we are skipped anyways. + let mut expected_blocks = expected_blocks + .into_iter() + .filter(|block| block.round() <= SYNC_MISSING_BLOCK_ROUND_THRESHOLD) + .collect::>(); + + for chunk in expected_blocks.chunks(MAX_BLOCKS_PER_FETCH) { + network_client + .stub_fetch_blocks( + chunk.to_vec(), + AuthorityIndex::new_for_test(1), + Some(FETCH_REQUEST_TIMEOUT), + ) + .await; + + network_client + .stub_fetch_blocks(chunk.to_vec(), AuthorityIndex::new_for_test(2), None) + .await; + } + + // Now create some blocks to simulate a commit lag + let round = context.parameters.commit_sync_batch_size * COMMIT_LAG_MULTIPLIER * 2; + let commit_index: CommitIndex = round - 1; + let blocks = (0..4) + .map(|authority| { + let commit_votes = vec![CommitVote::new(commit_index, CommitDigest::MIN)]; + let block = TestBlock::new(round, authority) + .set_commit_votes(commit_votes) + .build(); + + VerifiedBlock::new_for_test(block) + }) + .collect::>(); + + // Pass them through the commit vote monitor - so now there will be a big commit lag to prevent + // the scheduled synchronizer from running + for block in blocks { + commit_vote_monitor.observe_block(&block); + } + + // WHEN start the synchronizer and wait for a couple of seconds where normally the synchronizer should have kicked in. + let _handle = Synchronizer::start( + network_client.clone(), + context.clone(), + core_dispatcher.clone(), + commit_vote_monitor.clone(), + block_verifier.clone(), + dag_state.clone(), + false, + ); + + sleep(4 * FETCH_REQUEST_TIMEOUT).await; + + // We should be in commit lag mode, but since there are missing blocks within the acceptable round thresholds those ones should be fetched. Nothing above. + let mut added_blocks = core_dispatcher.get_add_blocks().await; + + added_blocks.sort_by_key(|block| block.reference()); + expected_blocks.sort_by_key(|block| block.reference()); + + assert_eq!(added_blocks, expected_blocks); + } + + #[tokio::test(flavor = "current_thread", start_paused = true)] + async fn synchronizer_periodic_task_when_commit_lagging_gets_disabled() { + // GIVEN + let (context, _) = Context::new_for_test(4); + let context = Arc::new(context); + let block_verifier = Arc::new(NoopBlockVerifier {}); + let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default()); + let network_client = Arc::new(MockNetworkClient::default()); + let store = Arc::new(MemStore::new()); + let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store))); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); + + // AND stub some missing blocks. The highest accepted round is 0. Create blocks that are above the threshold sync. + let mut expected_blocks = (SYNC_MISSING_BLOCK_ROUND_THRESHOLD * 2 + ..SYNC_MISSING_BLOCK_ROUND_THRESHOLD * 3) .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build())) .collect::>(); let missing_blocks = expected_blocks @@ -1470,20 +1585,18 @@ mod tests { // AND stub the requests for authority 1 & 2 // Make the first authority timeout, so the second will be called. "We" are authority = 0, so // we are skipped anyways. - network_client - .stub_fetch_blocks( - expected_blocks.clone(), - AuthorityIndex::new_for_test(1), - Some(FETCH_REQUEST_TIMEOUT), - ) - .await; - network_client - .stub_fetch_blocks( - expected_blocks.clone(), - AuthorityIndex::new_for_test(2), - None, - ) - .await; + for chunk in expected_blocks.chunks(MAX_BLOCKS_PER_FETCH) { + network_client + .stub_fetch_blocks( + chunk.to_vec(), + AuthorityIndex::new_for_test(1), + Some(FETCH_REQUEST_TIMEOUT), + ) + .await; + network_client + .stub_fetch_blocks(chunk.to_vec(), AuthorityIndex::new_for_test(2), None) + .await; + } // Now create some blocks to simulate a commit lag let round = context.parameters.commit_sync_batch_size * COMMIT_LAG_MULTIPLIER * 2; @@ -1540,10 +1653,19 @@ mod tests { ); } + // Now stub again the missing blocks to fetch the exact same ones. + core_dispatcher + .stub_missing_blocks(missing_blocks.clone()) + .await; + sleep(2 * FETCH_REQUEST_TIMEOUT).await; // THEN the missing blocks should now be fetched and added to core - let added_blocks = core_dispatcher.get_add_blocks().await; + let mut added_blocks = core_dispatcher.get_add_blocks().await; + + added_blocks.sort_by_key(|block| block.reference()); + expected_blocks.sort_by_key(|block| block.reference()); + assert_eq!(added_blocks, expected_blocks); } diff --git a/consensus/core/src/test_dag_builder.rs b/consensus/core/src/test_dag_builder.rs index d669ed50d42f3..347321947c879 100644 --- a/consensus/core/src/test_dag_builder.rs +++ b/consensus/core/src/test_dag_builder.rs @@ -161,6 +161,8 @@ impl DagBuilder { sort_sub_dag_blocks(&mut to_commit); + let rejected_transactions = vec![vec![]; to_commit.len()]; + let commit = TrustedCommit::new_for_test( commit_index, CommitDigest::MIN, @@ -175,6 +177,7 @@ impl DagBuilder { let sub_dag = CommittedSubDag::new( leader_block_ref, to_commit, + rejected_transactions, timestamp_ms, commit.reference(), vec![], diff --git a/crates/sui-analytics-indexer/src/analytics_processor.rs b/crates/sui-analytics-indexer/src/analytics_processor.rs index 04089590ce5aa..1e55981dabafd 100644 --- a/crates/sui-analytics-indexer/src/analytics_processor.rs +++ b/crates/sui-analytics-indexer/src/analytics_processor.rs @@ -53,6 +53,7 @@ const CHECK_FILE_SIZE_ITERATION_CYCLE: u64 = 50; #[async_trait::async_trait] impl Worker for AnalyticsProcessor { + type Result = (); async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { // get epoch id, checkpoint sequence number and timestamp, those are important // indexes when operating on data diff --git a/crates/sui-analytics-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-analytics-indexer/src/handlers/checkpoint_handler.rs index 563c12ce8236e..b3dd2042637d9 100644 --- a/crates/sui-analytics-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/checkpoint_handler.rs @@ -25,6 +25,8 @@ struct State { #[async_trait::async_trait] impl Worker for CheckpointHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/df_handler.rs b/crates/sui-analytics-indexer/src/handlers/df_handler.rs index 5c0f8f2119124..f8dfdea85a039 100644 --- a/crates/sui-analytics-indexer/src/handlers/df_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/df_handler.rs @@ -39,6 +39,8 @@ struct State { #[async_trait::async_trait] impl Worker for DynamicFieldHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/event_handler.rs b/crates/sui-analytics-indexer/src/handlers/event_handler.rs index 4e78f99693d31..bd2cdf6776350 100644 --- a/crates/sui-analytics-indexer/src/handlers/event_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/event_handler.rs @@ -33,6 +33,8 @@ struct State { #[async_trait::async_trait] impl Worker for EventHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/mod.rs b/crates/sui-analytics-indexer/src/handlers/mod.rs index 420335695f29c..ac4026dcac4ee 100644 --- a/crates/sui-analytics-indexer/src/handlers/mod.rs +++ b/crates/sui-analytics-indexer/src/handlers/mod.rs @@ -38,7 +38,7 @@ const WRAPPED_INDEXING_DISALLOW_LIST: [&str; 4] = [ ]; #[async_trait::async_trait] -pub trait AnalyticsHandler: Worker { +pub trait AnalyticsHandler: Worker { /// Read back rows which are ready to be persisted. This function /// will be invoked by the analytics processor after every call to /// process_checkpoint diff --git a/crates/sui-analytics-indexer/src/handlers/move_call_handler.rs b/crates/sui-analytics-indexer/src/handlers/move_call_handler.rs index d1c2cba3e85e3..93c018164fcb3 100644 --- a/crates/sui-analytics-indexer/src/handlers/move_call_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/move_call_handler.rs @@ -23,6 +23,8 @@ struct State { #[async_trait::async_trait] impl Worker for MoveCallHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/object_handler.rs b/crates/sui-analytics-indexer/src/handlers/object_handler.rs index d03a9f20acd89..f8b4f26f3023b 100644 --- a/crates/sui-analytics-indexer/src/handlers/object_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/object_handler.rs @@ -35,6 +35,8 @@ struct State { #[async_trait::async_trait] impl Worker for ObjectHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/package_handler.rs b/crates/sui-analytics-indexer/src/handlers/package_handler.rs index 6f0fa0f491b71..bfb6bcfdccd88 100644 --- a/crates/sui-analytics-indexer/src/handlers/package_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/package_handler.rs @@ -23,6 +23,8 @@ struct State { #[async_trait::async_trait] impl Worker for PackageHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/transaction_handler.rs b/crates/sui-analytics-indexer/src/handlers/transaction_handler.rs index 0da0f8b21f270..2ade6f8f1c8db 100644 --- a/crates/sui-analytics-indexer/src/handlers/transaction_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/transaction_handler.rs @@ -28,6 +28,8 @@ pub(crate) struct State { #[async_trait::async_trait] impl Worker for TransactionHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/transaction_objects_handler.rs b/crates/sui-analytics-indexer/src/handlers/transaction_objects_handler.rs index ee6004f7052c1..e9fbc38542fdb 100644 --- a/crates/sui-analytics-indexer/src/handlers/transaction_objects_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/transaction_objects_handler.rs @@ -24,6 +24,8 @@ struct State { #[async_trait::async_trait] impl Worker for TransactionObjectsHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/handlers/wrapped_object_handler.rs b/crates/sui-analytics-indexer/src/handlers/wrapped_object_handler.rs index 9734b10f1ffad..8887df15ffc0c 100644 --- a/crates/sui-analytics-indexer/src/handlers/wrapped_object_handler.rs +++ b/crates/sui-analytics-indexer/src/handlers/wrapped_object_handler.rs @@ -30,6 +30,8 @@ struct State { #[async_trait::async_trait] impl Worker for WrappedObjectHandler { + type Result = (); + async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { let CheckpointData { checkpoint_summary, diff --git a/crates/sui-analytics-indexer/src/lib.rs b/crates/sui-analytics-indexer/src/lib.rs index 86b3bc1e24004..579a057d5f9eb 100644 --- a/crates/sui-analytics-indexer/src/lib.rs +++ b/crates/sui-analytics-indexer/src/lib.rs @@ -487,12 +487,14 @@ impl FileMetadata { } pub struct Processor { - pub processor: Box, + pub processor: Box>, pub starting_checkpoint_seq_num: CheckpointSequenceNumber, } #[async_trait::async_trait] impl Worker for Processor { + type Result = (); + #[inline] async fn process_checkpoint(&self, checkpoint_data: &CheckpointData) -> Result<()> { self.processor.process_checkpoint(checkpoint_data).await diff --git a/crates/sui-authority-aggregation/src/lib.rs b/crates/sui-authority-aggregation/src/lib.rs index d1b05f5163e0a..15aae583d5487 100644 --- a/crates/sui-authority-aggregation/src/lib.rs +++ b/crates/sui-authority-aggregation/src/lib.rs @@ -7,7 +7,7 @@ use mysten_metrics::monitored_future; use std::collections::{BTreeMap, BTreeSet}; use std::sync::Arc; -use std::time::Duration; +use std::time::{Duration, Instant}; use sui_types::base_types::ConciseableName; use sui_types::committee::{CommitteeTrait, StakeUnit}; @@ -15,13 +15,25 @@ use tokio::time::timeout; pub type AsyncResult<'a, T, E> = BoxFuture<'a, Result>; +pub struct SigRequestPrefs { + pub ordering_pref: BTreeSet, + pub prefetch_timeout: Duration, +} + pub enum ReduceOutput { Continue(S), - ContinueWithTimeout(S, Duration), Failed(S), Success(R), } +/// This function takes an initial state, than executes an asynchronous function (FMap) for each +/// authority, and folds the results as they become available into the state using an async function (FReduce). +/// +/// prefetch_timeout: the minimum amount of time to spend trying to gather results from all authorities +/// before falling back to arrival order. +/// +/// total_timeout: the maximum amount of total time to wait for results from all authorities, including +/// time spent prefetching. pub async fn quorum_map_then_reduce_with_timeout_and_prefs< 'a, C, @@ -36,11 +48,11 @@ pub async fn quorum_map_then_reduce_with_timeout_and_prefs< >( committee: Arc, authority_clients: Arc>>, - authority_preferences: Option<&BTreeSet>, + authority_preferences: Option>, initial_state: S, map_each_authority: FMap, reduce_result: FReduce, - initial_timeout: Duration, + total_timeout: Duration, ) -> Result< ( R, @@ -54,10 +66,22 @@ where FMap: FnOnce(K, Arc) -> AsyncResult<'a, V, E> + Clone + 'a, FReduce: Fn(S, K, StakeUnit, Result) -> BoxFuture<'a, ReduceOutput>, { - let authorities_shuffled = committee.shuffle_by_stake(authority_preferences, None); + let (preference, prefetch_timeout) = if let Some(SigRequestPrefs { + ordering_pref, + prefetch_timeout, + }) = authority_preferences + { + (Some(ordering_pref), Some(prefetch_timeout)) + } else { + (None, None) + }; + let authorities_shuffled = committee.shuffle_by_stake(preference.as_ref(), None); + let mut accumulated_state = initial_state; + let mut total_timeout = total_timeout; // First, execute in parallel for each authority FMap. let mut responses: futures::stream::FuturesUnordered<_> = authorities_shuffled + .clone() .into_iter() .map(|name| { let client = authority_clients[&name].clone(); @@ -65,22 +89,66 @@ where monitored_future!(async move { (name.clone(), execute(name, client).await,) }) }) .collect(); + if let Some(prefetch_timeout) = prefetch_timeout { + let elapsed = Instant::now(); + let prefetch_sleep = tokio::time::sleep(prefetch_timeout); + let mut authority_to_result: BTreeMap> = BTreeMap::new(); + tokio::pin!(prefetch_sleep); + // get all the sigs we can within prefetch_timeout + loop { + tokio::select! { + resp = responses.next() => { + match resp { + Some((authority_name, result)) => { + authority_to_result.insert(authority_name, result); + } + None => { + // we have processed responses from the full committee so can stop early + break; + } + } + } + _ = &mut prefetch_sleep => { + break; + } + } + } + // process what we have up to this point + for authority_name in authorities_shuffled { + let authority_weight = committee.weight(&authority_name); + if let Some(result) = authority_to_result.remove(&authority_name) { + accumulated_state = match reduce_result( + accumulated_state, + authority_name, + authority_weight, + result, + ) + .await + { + // In the first two cases we are told to continue the iteration. + ReduceOutput::Continue(state) => state, + ReduceOutput::Failed(state) => { + return Err(state); + } + ReduceOutput::Success(result) => { + // The reducer tells us that we have the result needed. Just return it. + return Ok((result, responses)); + } + }; + } + } + // if we got here, fallback through the if statement to continue in arrival order on + // the remaining validators + total_timeout = total_timeout.saturating_sub(elapsed.elapsed()); + } - let mut current_timeout = initial_timeout; - let mut accumulated_state = initial_state; - // Then, as results become available fold them into the state using FReduce. - while let Ok(Some((authority_name, result))) = timeout(current_timeout, responses.next()).await - { + // As results become available fold them into the state using FReduce. + while let Ok(Some((authority_name, result))) = timeout(total_timeout, responses.next()).await { let authority_weight = committee.weight(&authority_name); accumulated_state = match reduce_result(accumulated_state, authority_name, authority_weight, result).await { // In the first two cases we are told to continue the iteration. ReduceOutput::Continue(state) => state, - ReduceOutput::ContinueWithTimeout(state, duration) => { - // Adjust the waiting timeout. - current_timeout = duration; - state - } ReduceOutput::Failed(state) => { return Err(state); } @@ -104,8 +172,7 @@ where /// /// FReduce returns a result to a ReduceOutput. If the result is Err the function /// shortcuts and the Err is returned. An Ok ReduceOutput result can be used to shortcut and return -/// the resulting state (ReduceOutput::End), continue the folding as new states arrive (ReduceOutput::Continue), -/// or continue with a timeout maximum waiting time (ReduceOutput::ContinueWithTimeout). +/// the resulting state (ReduceOutput::End), continue the folding as new states arrive (ReduceOutput::Continue). /// /// This function provides a flexible way to communicate with a quorum of authorities, processing and /// processing their results into a safe overall result, and also safely allowing operations to continue diff --git a/crates/sui-benchmark/tests/simtest.rs b/crates/sui-benchmark/tests/simtest.rs index b7d2e189da3a2..9f3e36b1998bd 100644 --- a/crates/sui-benchmark/tests/simtest.rs +++ b/crates/sui-benchmark/tests/simtest.rs @@ -463,10 +463,14 @@ mod test { let max_deferral_rounds; { let mut rng = thread_rng(); - mode = if rng.gen_bool(0.5) { + mode = if rng.gen_bool(0.33) { PerObjectCongestionControlMode::TotalGasBudget } else { - PerObjectCongestionControlMode::TotalTxCount + if rng.gen_bool(0.5) { + PerObjectCongestionControlMode::TotalTxCount + } else { + PerObjectCongestionControlMode::TotalGasBudgetWithCap + } }; checkpoint_budget_factor = rng.gen_range(1..20); txn_count_limit = rng.gen_range(1..=10); @@ -504,6 +508,14 @@ mod test { txn_count_limit ); }, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => { + let total_gas_limit = checkpoint_budget_factor + * DEFAULT_VALIDATOR_GAS_PRICE + * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE; + config.set_max_accumulated_txn_cost_per_object_in_narwhal_commit_for_testing(total_gas_limit); + config.set_max_accumulated_txn_cost_per_object_in_mysticeti_commit_for_testing(total_gas_limit); + config.set_gas_budget_based_txn_cost_cap_factor_for_testing(total_gas_limit); // Not sure what to set here. + }, } config.set_max_deferral_rounds_for_congestion_control_for_testing(max_deferral_rounds); config diff --git a/crates/sui-bridge-indexer/config.yaml b/crates/sui-bridge-indexer/config.yaml index 5efd22b251957..5263c915279a5 100644 --- a/crates/sui-bridge-indexer/config.yaml +++ b/crates/sui-bridge-indexer/config.yaml @@ -20,7 +20,3 @@ # metric_url: # Client metric port # metric_port: -# checkpoint size of each backfill worker, use 432000 for 1 worker per day, assume 5 checkpoint per second -# back_fill_lot_size: -# Optional starting checkpoint for realtime ingestion task -# resume_from_checkpoint: \ No newline at end of file diff --git a/crates/sui-bridge-indexer/src/sui_datasource.rs b/crates/sui-bridge-indexer/src/sui_datasource.rs index 6901f5fb3e169..c292992298670 100644 --- a/crates/sui-bridge-indexer/src/sui_datasource.rs +++ b/crates/sui-bridge-indexer/src/sui_datasource.rs @@ -196,6 +196,8 @@ pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); #[async_trait] impl Worker for IndexerWorker { + type Result = (); + async fn process_checkpoint(&self, checkpoint: &SuiCheckpointData) -> anyhow::Result<()> { tracing::trace!( "Received checkpoint [{}] {}: {}", diff --git a/crates/sui-bridge/src/action_executor.rs b/crates/sui-bridge/src/action_executor.rs index 26eaf0b6b6518..e8d54728e4011 100644 --- a/crates/sui-bridge/src/action_executor.rs +++ b/crates/sui-bridge/src/action_executor.rs @@ -1360,6 +1360,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Ok(signed_action.clone()), + None, ); signed_actions.insert(secret.public().into(), signed_action.into_sig().signature); } @@ -1376,6 +1377,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Err(BridgeError::RestAPIError("small issue".into())), + None, ); } } diff --git a/crates/sui-bridge/src/client/bridge_authority_aggregator.rs b/crates/sui-bridge/src/client/bridge_authority_aggregator.rs index 882e689f341f2..1de1c41635a7e 100644 --- a/crates/sui-bridge/src/client/bridge_authority_aggregator.rs +++ b/crates/sui-bridge/src/client/bridge_authority_aggregator.rs @@ -17,13 +17,16 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; use std::sync::Arc; use std::time::Duration; -use sui_authority_aggregation::quorum_map_then_reduce_with_timeout_and_prefs; use sui_authority_aggregation::ReduceOutput; +use sui_authority_aggregation::{quorum_map_then_reduce_with_timeout_and_prefs, SigRequestPrefs}; use sui_types::base_types::ConciseableName; use sui_types::committee::StakeUnit; use sui_types::committee::TOTAL_VOTING_POWER; use tracing::{error, info, warn}; +const TOTAL_TIMEOUT_MS: u64 = 5000; +const PREFETCH_TIMEOUT_MS: u64 = 1500; + pub struct BridgeAuthorityAggregator { pub committee: Arc, pub clients: Arc>>, @@ -72,6 +75,7 @@ impl BridgeAuthorityAggregator { self.committee.clone(), self.clients.clone(), state, + Duration::from_secs(PREFETCH_TIMEOUT_MS), ) .await } @@ -167,6 +171,7 @@ async fn request_sign_bridge_action_into_certification( committee: Arc, clients: Arc>>, state: GetSigsState, + prefetch_timeout: Duration, ) -> BridgeResult { // `preferences` is used as a trick here to influence the order of validators to be requested. // * if `Some(_)`, then we will request validators in the order of the voting power. @@ -175,20 +180,26 @@ async fn request_sign_bridge_action_into_certification( // we pass in `Some` to make sure the validators with higher voting power are requested first // to save gas cost. let preference = match action { - BridgeAction::SuiToEthBridgeAction(_) => Some(BTreeSet::new()), + BridgeAction::SuiToEthBridgeAction(_) => Some(SigRequestPrefs { + ordering_pref: BTreeSet::new(), + prefetch_timeout, + }), BridgeAction::EthToSuiBridgeAction(_) => None, _ => { if action.chain_id().is_sui_chain() { None } else { - Some(BTreeSet::new()) + Some(SigRequestPrefs { + ordering_pref: BTreeSet::new(), + prefetch_timeout, + }) } } }; let (result, _) = quorum_map_then_reduce_with_timeout_and_prefs( committee, clients, - preference.as_ref(), + preference, state, |_name, client| { Box::pin(async move { client.request_sign_bridge_action(action.clone()).await }) @@ -234,8 +245,7 @@ async fn request_sign_bridge_action_into_certification( } }) }, - // A herustic timeout, we expect the signing to finish within 5 seconds - Duration::from_secs(5), + Duration::from_secs(TOTAL_TIMEOUT_MS), ) .await .map_err(|state| { @@ -362,21 +372,25 @@ mod tests { sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[0])), + None, ); mock1.add_sui_event_response( sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[1])), + None, ); mock2.add_sui_event_response( sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[2])), + None, ); mock3.add_sui_event_response( sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[3])), + None, ); agg.request_committee_signatures(action.clone()) .await @@ -387,6 +401,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Err(BridgeError::RestAPIError("".into())), + None, ); agg.request_committee_signatures(action.clone()) .await @@ -397,6 +412,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Err(BridgeError::RestAPIError("".into())), + None, ); agg.request_committee_signatures(action.clone()) .await @@ -407,6 +423,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Err(BridgeError::RestAPIError("".into())), + None, ); let err = agg .request_committee_signatures(action.clone()) @@ -418,6 +435,176 @@ mod tests { )); } + #[tokio::test] + async fn test_bridge_auth_agg_optimized() { + telemetry_subscribers::init_for_testing(); + + let mock0 = BridgeRequestMockHandler::new(); + let mock1 = BridgeRequestMockHandler::new(); + let mock2 = BridgeRequestMockHandler::new(); + let mock3 = BridgeRequestMockHandler::new(); + let mock4 = BridgeRequestMockHandler::new(); + let mock5 = BridgeRequestMockHandler::new(); + let mock6 = BridgeRequestMockHandler::new(); + let mock7 = BridgeRequestMockHandler::new(); + let mock8 = BridgeRequestMockHandler::new(); + + // start servers - there is only one permutation of size 2 (1112, 2222) that will achieve quorum + let (_handles, authorities, secrets) = get_test_authorities_and_run_mock_bridge_server( + vec![666, 1000, 900, 900, 900, 900, 900, 1612, 2222], + vec![ + mock0.clone(), + mock1.clone(), + mock2.clone(), + mock3.clone(), + mock4.clone(), + mock5.clone(), + mock6.clone(), + mock7.clone(), + mock8.clone(), + ], + ); + + let authorities_clone = authorities.clone(); + let committee = Arc::new(BridgeCommittee::new(authorities_clone).unwrap()); + + let agg = BridgeAuthorityAggregator::new(committee.clone()); + + let sui_tx_digest = TransactionDigest::random(); + let sui_tx_event_index = 0; + let nonce = 0; + let amount = 1000; + let action = get_test_sui_to_eth_bridge_action( + Some(sui_tx_digest), + Some(sui_tx_event_index), + Some(nonce), + Some(amount), + None, + None, + None, + ); + + // All authorities return signatures + mock0.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[0])), + Some(Duration::from_millis(200)), + ); + mock1.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[1])), + Some(Duration::from_millis(200)), + ); + mock2.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[2])), + Some(Duration::from_millis(700)), + ); + mock3.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[3])), + Some(Duration::from_millis(700)), + ); + mock4.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[4])), + Some(Duration::from_millis(700)), + ); + mock5.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[5])), + Some(Duration::from_millis(700)), + ); + mock6.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[6])), + Some(Duration::from_millis(700)), + ); + mock7.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[7])), + Some(Duration::from_millis(900)), + ); + mock8.add_sui_event_response( + sui_tx_digest, + sui_tx_event_index, + Ok(sign_action_with_key(&action, &secrets[8])), + Some(Duration::from_millis(1_500)), + ); + + // we should receive all signatures in time, but only aggregate 2 authorities + // to achieve quorum + let state = GetSigsState::new(action.approval_threshold(), committee.clone()); + let resp = request_sign_bridge_action_into_certification( + action.clone(), + agg.committee.clone(), + agg.clients.clone(), + state, + Duration::from_millis(2_000), + ) + .await + .unwrap(); + let sig_keys = resp.auth_sig().signatures.keys().collect::>(); + assert_eq!(sig_keys.len(), 2); + assert!(sig_keys.contains(&authorities[7].pubkey_bytes())); + assert!(sig_keys.contains(&authorities[8].pubkey_bytes())); + + // we should receive all but the highest stake signatures in time, but still be able to + // achieve quorum with 3 sigs + let state = GetSigsState::new(action.approval_threshold(), committee.clone()); + let resp = request_sign_bridge_action_into_certification( + action.clone(), + agg.committee.clone(), + agg.clients.clone(), + state, + Duration::from_millis(1_200), + ) + .await + .unwrap(); + let sig_keys = resp.auth_sig().signatures.keys().collect::>(); + assert_eq!(sig_keys.len(), 3); + assert!(sig_keys.contains(&authorities[7].pubkey_bytes())); + // this should not have come in time + assert!(!sig_keys.contains(&authorities[8].pubkey_bytes())); + + // we should have fallen back to arrival order given that we timeout before we reach quorum + let state = GetSigsState::new(action.approval_threshold(), committee.clone()); + let start = std::time::Instant::now(); + let resp = request_sign_bridge_action_into_certification( + action.clone(), + agg.committee.clone(), + agg.clients.clone(), + state, + Duration::from_millis(500), + ) + .await + .unwrap(); + let elapsed = start.elapsed(); + assert!( + elapsed >= Duration::from_millis(700), + "Expected to have to wait at least 700ms to fallback to arrival order and achieve quorum, but was {:?}", + elapsed + ); + let sig_keys = resp.auth_sig().signatures.keys().collect::>(); + assert_eq!(sig_keys.len(), 4); + // These two do not make it on time initially, and then we should be able + // to achieve quorum before these ultimately arrive + assert!(!sig_keys.contains(&authorities[7].pubkey_bytes())); + assert!(!sig_keys.contains(&authorities[8].pubkey_bytes())); + // These were the first two to respond, and should be immediately + // included once we fallback to arrival order + assert!(sig_keys.contains(&authorities[0].pubkey_bytes())); + assert!(sig_keys.contains(&authorities[1].pubkey_bytes())); + } + #[tokio::test] async fn test_bridge_auth_agg_more_cases() { telemetry_subscribers::init_for_testing(); @@ -460,11 +647,13 @@ mod tests { sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[2])), + None, ); mock3.add_sui_event_response( sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[3])), + None, ); let certified = agg .request_committee_signatures(action.clone()) @@ -489,6 +678,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Err(BridgeError::RestAPIError("".into())), + None, ); let err = agg .request_committee_signatures(action.clone()) @@ -504,6 +694,7 @@ mod tests { sui_tx_digest, sui_tx_event_index, Ok(sign_action_with_key(&action, &secrets[2])), + None, ); let err = agg .request_committee_signatures(action.clone()) diff --git a/crates/sui-bridge/src/client/bridge_client.rs b/crates/sui-bridge/src/client/bridge_client.rs index 08bbd50984672..83c11e73ba995 100644 --- a/crates/sui-bridge/src/client/bridge_client.rs +++ b/crates/sui-bridge/src/client/bridge_client.rs @@ -341,7 +341,7 @@ mod tests { ); let sig = BridgeAuthoritySignInfo::new(&action, &secret); let signed_event = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig.clone()); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event.clone())); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event.clone()), None); // success client @@ -362,7 +362,7 @@ mod tests { let wrong_sig = BridgeAuthoritySignInfo::new(&action2, &secret); let wrong_signed_action = SignedBridgeAction::new_from_data_and_sig(action2.clone(), wrong_sig.clone()); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(wrong_signed_action)); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(wrong_signed_action), None); let err = client .request_sign_bridge_action(action.clone()) .await @@ -372,7 +372,7 @@ mod tests { // The action matches but the signature is wrong, fail let wrong_signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), wrong_sig); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(wrong_signed_action)); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(wrong_signed_action), None); let err = client .request_sign_bridge_action(action.clone()) .await @@ -389,7 +389,7 @@ mod tests { BridgeCommittee::new(vec![authority_blocklisted.clone(), authority2.clone()]).unwrap(), ); client.update_committee(committee2); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event)); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event), None); let err = client .request_sign_bridge_action(action.clone()) @@ -404,7 +404,7 @@ mod tests { // signed by a different authority in committee would fail let sig2 = BridgeAuthoritySignInfo::new(&action, &secret2); let signed_event2 = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig2.clone()); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event2)); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event2), None); let err = client .request_sign_bridge_action(action.clone()) .await @@ -416,7 +416,7 @@ mod tests { let secret3 = Arc::pin(kp3); let sig3 = BridgeAuthoritySignInfo::new(&action, &secret3); let signed_event3 = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig3); - mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event3)); + mock_handler.add_sui_event_response(tx_digest, event_idx, Ok(signed_event3), None); let err = client .request_sign_bridge_action(action.clone()) .await diff --git a/crates/sui-bridge/src/server/mock_handler.rs b/crates/sui-bridge/src/server/mock_handler.rs index f8b5e01fc912c..71fbf1c7c124f 100644 --- a/crates/sui-bridge/src/server/mock_handler.rs +++ b/crates/sui-bridge/src/server/mock_handler.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use std::time::Duration; use crate::crypto::BridgeAuthorityKeyPair; use crate::crypto::BridgeAuthoritySignInfo; @@ -28,8 +29,11 @@ use super::make_router; #[derive(Clone)] pub struct BridgeRequestMockHandler { signer: Arc>>, - sui_token_events: - Arc>>>, + sui_token_events: Arc< + Mutex< + HashMap<(TransactionDigest, u16), (BridgeResult, Option)>, + >, + >, sui_token_events_requested: Arc>>, } @@ -47,11 +51,12 @@ impl BridgeRequestMockHandler { tx_digest: TransactionDigest, idx: u16, response: BridgeResult, + delay: Option, ) { self.sui_token_events .lock() .unwrap() - .insert((tx_digest, idx), response); + .insert((tx_digest, idx), (response, delay)); } pub fn get_sui_token_events_requested( @@ -95,26 +100,29 @@ impl BridgeRequestHandlerTrait for BridgeRequestMockHandler { ) -> Result, BridgeError> { let tx_digest = TransactionDigest::from_str(&tx_digest_base58) .map_err(|_e| BridgeError::InvalidTxHash)?; - let preset = self.sui_token_events.lock().unwrap(); - if !preset.contains_key(&(tx_digest, event_idx)) { - // Ok to panic in test - panic!( - "No preset handle_sui_tx_digest result for tx_digest: {}, event_idx: {}", - tx_digest, event_idx - ); + let (result, delay) = { + let preset = self.sui_token_events.lock().unwrap(); + if !preset.contains_key(&(tx_digest, event_idx)) { + // Ok to panic in test + panic!( + "No preset handle_sui_tx_digest result for tx_digest: {}, event_idx: {}", + tx_digest, event_idx + ); + } + let mut requested = self.sui_token_events_requested.lock().unwrap(); + let entry = requested.entry((tx_digest, event_idx)).or_default(); + *entry += 1; + let (result, delay) = preset.get(&(tx_digest, event_idx)).unwrap(); + (result.clone(), *delay) + }; + if let Some(delay) = delay { + tokio::time::sleep(delay).await; } - let mut requested = self.sui_token_events_requested.lock().unwrap(); - let entry = requested.entry((tx_digest, event_idx)).or_default(); - *entry += 1; - let result = preset.get(&(tx_digest, event_idx)).unwrap(); - if let Err(e) = result { - return Err(e.clone()); - } - let signed_action: &sui_types::message_envelope::Envelope< + let signed_action: sui_types::message_envelope::Envelope< crate::types::BridgeAction, crate::crypto::BridgeAuthoritySignInfo, - > = result.as_ref().unwrap(); - Ok(Json(signed_action.clone())) + > = result?; + Ok(Json(signed_action)) } async fn handle_governance_action( diff --git a/crates/sui-config/src/node.rs b/crates/sui-config/src/node.rs index 556823e3f3752..1ab9ec678d364 100644 --- a/crates/sui-config/src/node.rs +++ b/crates/sui-config/src/node.rs @@ -286,7 +286,7 @@ pub fn default_zklogin_oauth_providers() -> BTreeMap> { "Onefc".to_string(), "FanTV".to_string(), "AwsTenant-region:us-east-1-tenant_id:us-east-1_LPSLCkC3A".to_string(), // test tenant in mysten aws - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), // ambrus, external partner + "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), // Ambrus, external partner "Arden".to_string(), // Arden partner "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), // Trace, external partner ]); @@ -297,12 +297,14 @@ pub fn default_zklogin_oauth_providers() -> BTreeMap> { "Facebook".to_string(), "Twitch".to_string(), "Apple".to_string(), - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), + "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), // Ambrus, external partner "KarrierOne".to_string(), "Credenza3".to_string(), "Playtron".to_string(), "Onefc".to_string(), "Threedos".to_string(), + "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), // Trace, external partner + "Arden".to_string(), ]); map.insert(Chain::Mainnet, providers.clone()); map.insert(Chain::Testnet, providers); @@ -874,7 +876,7 @@ pub struct AuthorityOverloadConfig { } fn default_max_txn_age_in_queue() -> Duration { - Duration::from_secs(1) + Duration::from_millis(200) } fn default_overload_monitor_interval() -> Duration { @@ -910,7 +912,7 @@ fn default_max_transaction_manager_queue_length() -> usize { } fn default_max_transaction_manager_per_object_queue_length() -> usize { - 100 + 20 } impl Default for AuthorityOverloadConfig { diff --git a/crates/sui-core/src/authority.rs b/crates/sui-core/src/authority.rs index 84cc36b3e75f0..10752097e09ed 100644 --- a/crates/sui-core/src/authority.rs +++ b/crates/sui-core/src/authority.rs @@ -52,6 +52,7 @@ use sui_types::layout_resolver::into_struct_layout; use sui_types::layout_resolver::LayoutResolver; use sui_types::messages_consensus::{AuthorityCapabilitiesV1, AuthorityCapabilitiesV2}; use sui_types::object::bounded_visitor::BoundedVisitor; +use sui_types::transaction_executor::SimulateTransactionResult; use tap::{TapFallible, TapOptional}; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::{mpsc, oneshot, RwLock}; @@ -278,7 +279,7 @@ pub struct AuthorityMetrics { post_processing_total_tx_had_event_processed: IntCounter, post_processing_total_failures: IntCounter, - /// Consensus handler metrics + /// Consensus commit and transaction handler metrics pub consensus_handler_processed: IntCounterVec, pub consensus_handler_transaction_sizes: HistogramVec, pub consensus_handler_num_low_scoring_authorities: IntGauge, @@ -292,6 +293,8 @@ pub struct AuthorityMetrics { pub consensus_committed_user_transactions: IntGaugeVec, pub consensus_calculated_throughput: IntGauge, pub consensus_calculated_throughput_profile: IntGauge, + pub consensus_transaction_handler_processed: IntCounterVec, + pub consensus_transaction_handler_fastpath_executions: IntCounter, pub limits_metrics: Arc, @@ -759,6 +762,17 @@ impl AuthorityMetrics { "The current active calculated throughput profile", registry ).unwrap(), + consensus_transaction_handler_processed: register_int_counter_vec_with_registry!( + "consensus_transaction_handler_processed", + "Number of transactions processed by consensus transaction handler, by whether they are certified or rejected.", + &["outcome"], + registry + ).unwrap(), + consensus_transaction_handler_fastpath_executions: register_int_counter_with_registry!( + "consensus_transaction_handler_fastpath_executions", + "Number of fastpath transactions sent for execution by consensus transaction handler", + registry, + ).unwrap(), execution_queueing_latency: LatencyObserver::new(), txn_ready_rate_tracker: Arc::new(Mutex::new(RateTracker::new(Duration::from_secs(10)))), execution_rate_tracker: Arc::new(Mutex::new(RateTracker::new(Duration::from_secs(10)))), @@ -1196,6 +1210,7 @@ impl AuthorityState { self.metrics.total_cert_attempts.inc(); + // TODO(fastpath): use a separate function to check if a transaction should be executed in fastpath. if !certificate.contains_shared_object() { // Shared object transactions need to be sequenced by the consensus before enqueueing // for execution, done in AuthorityPerEpochStore::handle_consensus_transaction(). @@ -1961,6 +1976,135 @@ impl AuthorityState { )) } + pub fn simulate_transaction( + &self, + transaction: TransactionData, + ) -> SuiResult { + if transaction.kind().is_system_tx() { + return Err(SuiError::UnsupportedFeatureError { + error: "simulate does not support system transactions".to_string(), + }); + } + + let epoch_store = self.load_epoch_store_one_call_per_task(); + if !self.is_fullnode(&epoch_store) { + return Err(SuiError::UnsupportedFeatureError { + error: "simulate is only supported on fullnodes".to_string(), + }); + } + + self.simulate_transaction_impl(&epoch_store, transaction) + } + + fn simulate_transaction_impl( + &self, + epoch_store: &AuthorityPerEpochStore, + transaction: TransactionData, + ) -> SuiResult { + // Cheap validity checks for a transaction, including input size limits. + transaction.validity_check_no_gas_check(epoch_store.protocol_config())?; + + let input_object_kinds = transaction.input_objects()?; + let receiving_object_refs = transaction.receiving_objects(); + + sui_transaction_checks::deny::check_transaction_for_signing( + &transaction, + &[], + &input_object_kinds, + &receiving_object_refs, + &self.config.transaction_deny_config, + self.get_backing_package_store().as_ref(), + )?; + + let (input_objects, receiving_objects) = self.input_loader.read_objects_for_signing( + // We don't want to cache this transaction since it's a dry run. + None, + &input_object_kinds, + &receiving_object_refs, + epoch_store.epoch(), + )?; + + // make a gas object if one was not provided + let mut gas_object_refs = transaction.gas().to_vec(); + let ((gas_status, checked_input_objects), mock_gas) = if transaction.gas().is_empty() { + let sender = transaction.sender(); + // use a 1B sui coin + const MIST_TO_SUI: u64 = 1_000_000_000; + const DRY_RUN_SUI: u64 = 1_000_000_000; + let max_coin_value = MIST_TO_SUI * DRY_RUN_SUI; + let gas_object_id = ObjectID::MAX; + let gas_object = Object::new_move( + MoveObject::new_gas_coin(OBJECT_START_VERSION, gas_object_id, max_coin_value), + Owner::AddressOwner(sender), + TransactionDigest::genesis_marker(), + ); + let gas_object_ref = gas_object.compute_object_reference(); + gas_object_refs = vec![gas_object_ref]; + ( + sui_transaction_checks::check_transaction_input_with_given_gas( + epoch_store.protocol_config(), + epoch_store.reference_gas_price(), + &transaction, + input_objects, + receiving_objects, + gas_object, + &self.metrics.bytecode_verifier_metrics, + &self.config.verifier_signing_config, + )?, + Some(gas_object_id), + ) + } else { + ( + sui_transaction_checks::check_transaction_input( + epoch_store.protocol_config(), + epoch_store.reference_gas_price(), + &transaction, + input_objects, + &receiving_objects, + &self.metrics.bytecode_verifier_metrics, + &self.config.verifier_signing_config, + )?, + None, + ) + }; + + let protocol_config = epoch_store.protocol_config(); + let (kind, signer, _) = transaction.execution_parts(); + + let silent = true; + let executor = sui_execution::executor(protocol_config, silent, None) + .expect("Creating an executor should not fail here"); + + let expensive_checks = false; + let (inner_temp_store, _, effects, _execution_error) = executor + .execute_transaction_to_effects( + self.get_backing_store().as_ref(), + protocol_config, + self.metrics.limits_metrics.clone(), + expensive_checks, + self.config.certificate_deny_config.certificate_deny_set(), + &epoch_store.epoch_start_config().epoch_data().epoch_id(), + epoch_store + .epoch_start_config() + .epoch_data() + .epoch_start_timestamp(), + checked_input_objects, + gas_object_refs, + gas_status, + kind, + signer, + transaction.digest(), + ); + + Ok(SimulateTransactionResult { + input_objects: inner_temp_store.input_objects, + output_objects: inner_temp_store.written, + events: effects.events_digest().map(|_| inner_temp_store.events), + effects, + mock_gas_id: mock_gas, + }) + } + /// The object ID for gas can be any object ID, even for an uncreated object #[allow(clippy::collapsible_else_if)] #[instrument(skip_all)] @@ -3979,6 +4123,14 @@ impl AuthorityState { limit, descending, )?, + // not using "_ =>" because we want to make sure we remember to add new variants here + EventFilter::Any(_) => { + return Err(SuiError::UserInputError { + error: UserInputError::Unsupported( + "'Any' queries are not supported by the fullnode.".to_string(), + ), + }) + } }; // skip one event if exclusive cursor is provided, diff --git a/crates/sui-core/src/authority/authority_per_epoch_store.rs b/crates/sui-core/src/authority/authority_per_epoch_store.rs index 25e4c1a341472..70c1e98bbb2e1 100644 --- a/crates/sui-core/src/authority/authority_per_epoch_store.rs +++ b/crates/sui-core/src/authority/authority_per_epoch_store.rs @@ -316,6 +316,11 @@ pub struct AuthorityPerEpochStore { executed_digests_notify_read: NotifyRead, + /// Get notified when a synced checkpoint has reached CheckpointExecutor. + synced_checkpoint_notify_read: NotifyRead, + /// Caches the highest synced checkpoint sequence number as this has been notified from the CheckpointExecutor + highest_synced_checkpoint: RwLock, + /// This is used to notify all epoch specific tasks that epoch has ended. epoch_alive_notify: NotifyOnce, @@ -876,6 +881,8 @@ impl AuthorityPerEpochStore { checkpoint_state_notify_read: NotifyRead::new(), running_root_notify_read: NotifyRead::new(), executed_digests_notify_read: NotifyRead::new(), + synced_checkpoint_notify_read: NotifyRead::new(), + highest_synced_checkpoint: RwLock::new(0), end_of_publish: Mutex::new(end_of_publish), pending_consensus_certificates: RwLock::new(pending_consensus_certificates), mutex_table: MutexTable::new(MUTEX_TABLE_SIZE), @@ -1878,20 +1885,26 @@ impl AuthorityPerEpochStore { .pending_consensus_transactions .multi_insert(key_value_pairs)?; - // TODO: lock once for all insert() calls. - for transaction in transactions { - if let ConsensusTransactionKind::CertifiedTransaction(cert) = &transaction.kind { - let state = lock.expect("Must pass reconfiguration lock when storing certificate"); - // Caller is responsible for performing graceful check - assert!( - state.should_accept_user_certs(), - "Reconfiguration state should allow accepting user transactions" - ); - self.pending_consensus_certificates - .write() - .insert(*cert.digest()); - } + // UserTransaction exists only when mysticeti_fastpath is enabled in protocol config. + let digests: Vec<_> = transactions + .iter() + .filter_map(|tx| match &tx.kind { + ConsensusTransactionKind::CertifiedTransaction(cert) => Some(cert.digest()), + ConsensusTransactionKind::UserTransaction(txn) => Some(txn.digest()), + _ => None, + }) + .collect(); + if !digests.is_empty() { + let state = lock.expect("Must pass reconfiguration lock when storing certificate"); + // Caller is responsible for performing graceful check + assert!( + state.should_accept_user_certs(), + "Reconfiguration state should allow accepting user transactions" + ); + let mut pending_consensus_certificates = self.pending_consensus_certificates.write(); + pending_consensus_certificates.extend(digests); } + Ok(()) } @@ -2031,6 +2044,33 @@ impl AuthorityPerEpochStore { Ok(()) } + /// Notifies that a synced checkpoint of sequence number `checkpoint_seq` is available. The source of the notification + /// is the CheckpointExecutor. The consumer here is guaranteed to be notified in sequence order. + pub fn notify_synced_checkpoint(&self, checkpoint_seq: CheckpointSequenceNumber) { + let mut highest_synced_checkpoint = self.highest_synced_checkpoint.write(); + *highest_synced_checkpoint = checkpoint_seq; + self.synced_checkpoint_notify_read + .notify(&checkpoint_seq, &()); + } + + /// Get notified when a synced checkpoint of sequence number `>= checkpoint_seq` is available. + pub async fn synced_checkpoint_notify( + &self, + checkpoint_seq: CheckpointSequenceNumber, + ) -> Result<(), SuiError> { + let registration = self + .synced_checkpoint_notify_read + .register_one(&checkpoint_seq); + { + let synced_checkpoint = self.highest_synced_checkpoint.read(); + if *synced_checkpoint >= checkpoint_seq { + return Ok(()); + } + } + registration.await; + Ok(()) + } + pub fn has_sent_end_of_publish(&self, authority: &AuthorityName) -> SuiResult { Ok(self .end_of_publish @@ -2768,7 +2808,7 @@ impl AuthorityPerEpochStore { .collect(); let ( - transactions_to_schedule, + verified_transactions, notifications, lock, final_round, @@ -2790,10 +2830,7 @@ impl AuthorityPerEpochStore { authority_metrics, ) .await?; - self.finish_consensus_certificate_process_with_batch( - &mut output, - &transactions_to_schedule, - )?; + self.finish_consensus_certificate_process_with_batch(&mut output, &verified_transactions)?; output.record_consensus_commit_stats(consensus_stats.clone()); // Create pending checkpoints if we are still accepting tx. @@ -2901,7 +2938,7 @@ impl AuthorityPerEpochStore { self.record_end_of_message_quorum_time_metric(); } - Ok(transactions_to_schedule) + Ok(verified_transactions) } // Adds the consensus commit prologue transaction to the beginning of input `transactions` to update @@ -3119,10 +3156,14 @@ impl AuthorityPerEpochStore { // they will be in different checkpoints. let mut shared_object_congestion_tracker = SharedObjectCongestionTracker::new( self.protocol_config().per_object_congestion_control_mode(), + self.protocol_config() + .gas_budget_based_txn_cost_cap_factor_as_option(), ); let mut shared_object_using_randomness_congestion_tracker = SharedObjectCongestionTracker::new( self.protocol_config().per_object_congestion_control_mode(), + self.protocol_config() + .gas_budget_based_txn_cost_cap_factor_as_option(), ); fail_point_arg!( @@ -3682,7 +3723,7 @@ impl AuthorityPerEpochStore { kind: ConsensusTransactionKind::UserTransaction(_tx), .. }) => { - // TODO: implement handling of unsigned user transactions. + // TODO(fastpath): implement handling of user transactions from consensus commits. Ok(ConsensusCertificateResult::Ignored) } SequencedConsensusTransactionKind::System(system_transaction) => { diff --git a/crates/sui-core/src/authority/authority_store.rs b/crates/sui-core/src/authority/authority_store.rs index a1ea916d1fd38..cb4dc555d975c 100644 --- a/crates/sui-core/src/authority/authority_store.rs +++ b/crates/sui-core/src/authority/authority_store.rs @@ -904,7 +904,7 @@ impl AuthorityStore { .iter() .map(|(id, new_object)| { let version = new_object.version(); - debug!(?id, ?version, "writing object"); + trace!(?id, ?version, "writing object"); let StoreObjectPair(store_object, indirect_object) = get_store_object_pair(new_object.clone(), self.indirect_objects_threshold); ( diff --git a/crates/sui-core/src/authority/shared_object_congestion_tracker.rs b/crates/sui-core/src/authority/shared_object_congestion_tracker.rs index 041e523594074..237d400114a43 100644 --- a/crates/sui-core/src/authority/shared_object_congestion_tracker.rs +++ b/crates/sui-core/src/authority/shared_object_congestion_tracker.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use sui_protocol_config::PerObjectCongestionControlMode; use sui_types::base_types::{ObjectID, TransactionDigest}; use sui_types::executable_transaction::VerifiedExecutableTransaction; -use sui_types::transaction::SharedInputObject; +use sui_types::transaction::{Argument, SharedInputObject, TransactionDataAPI}; // SharedObjectCongestionTracker stores the accumulated cost of executing transactions on an object, for // all transactions in a consensus commit. @@ -25,19 +25,25 @@ use sui_types::transaction::SharedInputObject; pub struct SharedObjectCongestionTracker { object_execution_cost: HashMap, mode: PerObjectCongestionControlMode, + gas_budget_based_txn_cost_cap_factor: Option, } impl SharedObjectCongestionTracker { - pub fn new(mode: PerObjectCongestionControlMode) -> Self { + pub fn new( + mode: PerObjectCongestionControlMode, + gas_budget_based_txn_cost_cap_factor: Option, + ) -> Self { Self { object_execution_cost: HashMap::new(), mode, + gas_budget_based_txn_cost_cap_factor, } } pub fn new_with_initial_value_for_test( init_values: &[(ObjectID, u64)], mode: PerObjectCongestionControlMode, + gas_budget_based_txn_cost_cap_factor: Option, ) -> Self { let mut object_execution_cost = HashMap::new(); for (object_id, total_cost) in init_values { @@ -46,6 +52,7 @@ impl SharedObjectCongestionTracker { Self { object_execution_cost, mode, + gas_budget_based_txn_cost_cap_factor, } } @@ -67,6 +74,9 @@ impl SharedObjectCongestionTracker { PerObjectCongestionControlMode::None => None, PerObjectCongestionControlMode::TotalGasBudget => Some(cert.gas_budget()), PerObjectCongestionControlMode::TotalTxCount => Some(1), + PerObjectCongestionControlMode::TotalGasBudgetWithCap => { + Some(std::cmp::min(cert.gas_budget(), self.get_tx_cost_cap(cert))) + } } } @@ -154,6 +164,25 @@ impl SharedObjectCongestionTracker { .copied() .unwrap_or(0) } + + fn get_tx_cost_cap(&self, cert: &VerifiedExecutableTransaction) -> u64 { + let mut number_of_move_call = 0; + let mut number_of_move_input = 0; + for command in cert.transaction_data().kind().iter_commands() { + if let sui_types::transaction::Command::MoveCall(move_call) = command { + number_of_move_call += 1; + for aug in move_call.arguments.iter() { + if let Argument::Input(_) = aug { + number_of_move_input += 1; + } + } + } + } + (number_of_move_call + number_of_move_input) as u64 + * self + .gas_budget_based_txn_cost_cap_factor + .expect("cap factor must be set if TotalGasBudgetWithCap mode is used.") + } } #[cfg(test)] @@ -164,7 +193,9 @@ mod object_cost_tests { use sui_test_transaction_builder::TestTransactionBuilder; use sui_types::base_types::{random_object_ref, SequenceNumber}; use sui_types::crypto::{get_key_pair, AccountKeyPair}; + use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder; use sui_types::transaction::{CallArg, ObjectArg, VerifiedTransaction}; + use sui_types::Identifier; fn construct_shared_input_objects(objects: &[(ObjectID, bool)]) -> Vec { objects @@ -187,6 +218,7 @@ mod object_cost_tests { SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(object_id_0, 5), (object_id_1, 10)], PerObjectCongestionControlMode::TotalGasBudget, + None, ); let shared_input_objects = construct_shared_input_objects(&[(object_id_0, false)]); @@ -257,11 +289,56 @@ mod object_cost_tests { ) } + fn build_programmable_transaction( + objects: &[(ObjectID, bool)], + number_of_commands: u64, + gas_budget: u64, + ) -> VerifiedExecutableTransaction { + let (sender, keypair): (_, AccountKeyPair) = get_key_pair(); + let gas_object = random_object_ref(); + + let package_id = ObjectID::random(); + let mut pt_builder = ProgrammableTransactionBuilder::new(); + let mut arguments = Vec::new(); + for object in objects { + arguments.push( + pt_builder + .obj(ObjectArg::SharedObject { + id: object.0, + initial_shared_version: SequenceNumber::new(), + mutable: object.1, + }) + .unwrap(), + ); + } + for _ in 0..number_of_commands { + pt_builder.programmable_move_call( + package_id, + Identifier::new("unimportant_module").unwrap(), + Identifier::new("unimportant_function").unwrap(), + vec![], + arguments.clone(), + ); + } + + let pt = pt_builder.finish(); + VerifiedExecutableTransaction::new_system( + VerifiedTransaction::new_unchecked( + TestTransactionBuilder::new(sender, gas_object, 1000) + .with_gas_budget(gas_budget) + .programmable(pt) + .build_and_sign(&keypair), + ), + 0, + ) + } + #[rstest] fn test_should_defer_return_correct_congested_objects( #[values( PerObjectCongestionControlMode::TotalGasBudget, - PerObjectCongestionControlMode::TotalTxCount + PerObjectCongestionControlMode::TotalTxCount, + PerObjectCongestionControlMode::TotalGasBudgetWithCap )] mode: PerObjectCongestionControlMode, ) { @@ -276,6 +353,7 @@ mod object_cost_tests { PerObjectCongestionControlMode::None => unreachable!(), PerObjectCongestionControlMode::TotalGasBudget => tx_gas_budget + 1, PerObjectCongestionControlMode::TotalTxCount => 2, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => tx_gas_budget - 1, }; let shared_object_congestion_tracker = match mode { @@ -288,6 +366,7 @@ mod object_cost_tests { SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(shared_obj_0, 10), (shared_obj_1, 1)], mode, + None, ) } PerObjectCongestionControlMode::TotalTxCount => { @@ -298,6 +377,18 @@ mod object_cost_tests { SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(shared_obj_0, 2), (shared_obj_1, 1)], mode, + None, + ) + } + PerObjectCongestionControlMode::TotalGasBudgetWithCap => { + // Construct object execution cost as following + // 1 10 + // object 0: | + // object 1: | + SharedObjectCongestionTracker::new_with_initial_value_for_test( + &[(shared_obj_0, 10), (shared_obj_1, 1)], + mode, + Some(45), // Make the cap just less than the gas budget, there are 1 objects in tx. ) } }; @@ -321,6 +412,8 @@ mod object_cost_tests { } // Read/write to object 1 should go through. + // When congestion control mode is TotalGasBudgetWithCap, even though the gas budget is over the limit, + // the cap should prevent the transaction from being deferred. for mutable in [true, false].iter() { let tx = build_transaction(&[(shared_obj_1, *mutable)], tx_gas_budget); assert!(shared_object_congestion_tracker @@ -361,7 +454,8 @@ mod object_cost_tests { fn test_should_defer_return_correct_deferral_key( #[values( PerObjectCongestionControlMode::TotalGasBudget, - PerObjectCongestionControlMode::TotalTxCount + PerObjectCongestionControlMode::TotalTxCount, + PerObjectCongestionControlMode::TotalGasBudgetWithCap )] mode: PerObjectCongestionControlMode, ) { @@ -369,7 +463,7 @@ mod object_cost_tests { let tx = build_transaction(&[(shared_obj_0, true)], 100); // Make should_defer_due_to_object_congestion always defer transactions. let max_accumulated_txn_cost_per_object_in_commit = 0; - let shared_object_congestion_tracker = SharedObjectCongestionTracker::new(mode); + let shared_object_congestion_tracker = SharedObjectCongestionTracker::new(mode, Some(2)); // Insert a random pre-existing transaction. let mut previously_deferred_tx_digests = HashMap::new(); @@ -460,7 +554,8 @@ mod object_cost_tests { fn test_bump_object_execution_cost( #[values( PerObjectCongestionControlMode::TotalGasBudget, - PerObjectCongestionControlMode::TotalTxCount + PerObjectCongestionControlMode::TotalTxCount, + PerObjectCongestionControlMode::TotalGasBudgetWithCap )] mode: PerObjectCongestionControlMode, ) { @@ -468,10 +563,13 @@ mod object_cost_tests { let object_id_1 = ObjectID::random(); let object_id_2 = ObjectID::random(); + let cap_factor = Some(1); + let mut shared_object_congestion_tracker = SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(object_id_0, 5), (object_id_1, 10)], mode, + cap_factor, ); assert_eq!(shared_object_congestion_tracker.max_cost(), 10); @@ -482,7 +580,8 @@ mod object_cost_tests { shared_object_congestion_tracker, SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(object_id_0, 5), (object_id_1, 10)], - mode + mode, + cap_factor, ) ); assert_eq!(shared_object_congestion_tracker.max_cost(), 10); @@ -494,12 +593,14 @@ mod object_cost_tests { PerObjectCongestionControlMode::None => unreachable!(), PerObjectCongestionControlMode::TotalGasBudget => 20, PerObjectCongestionControlMode::TotalTxCount => 11, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => 13, // 2 objects, 1 command. }; assert_eq!( shared_object_congestion_tracker, SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(object_id_0, expected_object_0_cost), (object_id_1, 10)], - mode + mode, + cap_factor, ) ); assert_eq!( @@ -520,6 +621,41 @@ mod object_cost_tests { PerObjectCongestionControlMode::None => unreachable!(), PerObjectCongestionControlMode::TotalGasBudget => 30, PerObjectCongestionControlMode::TotalTxCount => 12, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => 17, // 3 objects, 1 command + }; + shared_object_congestion_tracker.bump_object_execution_cost(&cert); + assert_eq!( + shared_object_congestion_tracker, + SharedObjectCongestionTracker::new_with_initial_value_for_test( + &[ + (object_id_0, expected_object_cost), + (object_id_1, expected_object_cost), + (object_id_2, expected_object_cost) + ], + mode, + cap_factor, + ) + ); + assert_eq!( + shared_object_congestion_tracker.max_cost(), + expected_object_cost + ); + + // Write to all objects with PTBs containing 7 commands. + let cert = build_programmable_transaction( + &[ + (object_id_0, true), + (object_id_1, true), + (object_id_2, true), + ], + 7, + 30, + ); + let expected_object_cost = match mode { + PerObjectCongestionControlMode::None => unreachable!(), + PerObjectCongestionControlMode::TotalGasBudget => 60, + PerObjectCongestionControlMode::TotalTxCount => 13, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => 45, // 3 objects, 7 commands }; shared_object_congestion_tracker.bump_object_execution_cost(&cert); assert_eq!( @@ -530,7 +666,8 @@ mod object_cost_tests { (object_id_1, expected_object_cost), (object_id_2, expected_object_cost) ], - mode + mode, + cap_factor, ) ); assert_eq!( diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs index 92c8dde74ff9e..f04ea86367464 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs @@ -532,6 +532,8 @@ impl CheckpointExecutor { let accumulator = self.accumulator.clone(); let state = self.state.clone(); + epoch_store.notify_synced_checkpoint(*checkpoint.sequence_number()); + pending.push_back(spawn_monitored_task!(async move { let epoch_store = epoch_store.clone(); let (tx_digests, checkpoint_acc) = loop { diff --git a/crates/sui-core/src/consensus_adapter.rs b/crates/sui-core/src/consensus_adapter.rs index acf7f550bc6e6..06f3241aedff3 100644 --- a/crates/sui-core/src/consensus_adapter.rs +++ b/crates/sui-core/src/consensus_adapter.rs @@ -2,15 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use arc_swap::{ArcSwap, ArcSwapOption}; -use bytes::Bytes; use dashmap::try_result::TryResult; use dashmap::DashMap; -use futures::future::{select, Either}; +use futures::future::{self, select, Either}; use futures::stream::FuturesUnordered; use futures::FutureExt; use futures::{pin_mut, StreamExt}; use itertools::Itertools; -use narwhal_types::{TransactionProto, TransactionsClient}; use parking_lot::RwLockReadGuard; use prometheus::Histogram; use prometheus::HistogramVec; @@ -34,7 +32,6 @@ use sui_types::base_types::TransactionDigest; use sui_types::committee::Committee; use sui_types::error::{SuiError, SuiResult}; -use tap::prelude::*; use tokio::sync::{Semaphore, SemaphorePermit}; use tokio::task::JoinHandle; use tokio::time::{self}; @@ -127,7 +124,7 @@ impl ConsensusAdapterMetrics { sequencing_certificate_latency: register_histogram_vec_with_registry!( "sequencing_certificate_latency", "The latency for sequencing a certificate.", - &["position", "tx_type"], + &["position", "tx_type", "processed_method"], SEQUENCING_CERTIFICATE_LATENCY_SEC_BUCKETS.to_vec(), registry, ).unwrap(), @@ -196,36 +193,6 @@ pub trait SubmitToConsensus: Sync + Send + 'static { epoch_store: &Arc, ) -> SuiResult; } - -#[async_trait::async_trait] -impl SubmitToConsensus for TransactionsClient { - async fn submit_to_consensus( - &self, - transactions: &[ConsensusTransaction], - _epoch_store: &Arc, - ) -> SuiResult { - let transactions_bytes = transactions - .iter() - .map(|t| { - let serialized = - bcs::to_bytes(t).expect("Serializing consensus transaction cannot fail"); - Bytes::from(serialized) - }) - .collect::>(); - self.clone() - .submit_transaction(TransactionProto { - transactions: transactions_bytes, - }) - .await - .map_err(|e| SuiError::ConsensusConnectionBroken(format!("{:?}", e))) - .tap_err(|r| { - // Will be logged by caller as well. - warn!("Submit transaction failed with: {:?}", r); - })?; - Ok(()) - } -} - /// Submit Sui certificates to the consensus. pub struct ConsensusAdapter { /// The network client connecting to the consensus node of this authority. @@ -817,13 +784,14 @@ impl ConsensusAdapter { .with_label_values(&[&bucket, tx_type]) .observe(ack_start.elapsed().as_secs_f64()); }; - match select(processed_waiter, submit_inner.boxed()).await { - Either::Left((processed, _submit_inner)) => processed, + + guard.processed_method = match select(processed_waiter, submit_inner.boxed()).await { + Either::Left((observed_via_consensus, _submit_inner)) => observed_via_consensus, Either::Right(((), processed_waiter)) => { debug!("Submitted {transaction_keys:?} to consensus"); processed_waiter.await } - } + }; } debug!("{transaction_keys:?} processed by consensus"); @@ -876,11 +844,12 @@ impl ConsensusAdapter { } /// Waits for transactions to appear either to consensus output or been executed via a checkpoint (state sync). + /// Returns the processed method, whether the transactions have been processed via consensus, or have been synced via checkpoint. async fn await_consensus_or_checkpoint( self: &Arc, transaction_keys: Vec, epoch_store: &Arc, - ) { + ) -> ProcessedMethod { let notifications = FuturesUnordered::new(); for transaction_key in transaction_keys { let transaction_digests = if let SequencedConsensusTransactionKey::External( @@ -892,6 +861,17 @@ impl ConsensusAdapter { vec![] }; + let checkpoint_synced_future = if let SequencedConsensusTransactionKey::External( + ConsensusTransactionKey::CheckpointSignature(_, checkpoint_sequence_number), + ) = transaction_key + { + // If the transaction is a checkpoint signature, we can also wait to get notified when a checkpoint with equal or higher sequence + // number has been already synced. This way we don't try to unnecessarily sequence the signature for an already verified checkpoint. + Either::Left(epoch_store.synced_checkpoint_notify(checkpoint_sequence_number)) + } else { + Either::Right(future::pending()) + }; + // We wait for each transaction individually to be processed by consensus or executed in a checkpoint. We could equally just // get notified in aggregate when all transactions are processed, but with this approach can get notified in a more fine-grained way // as transactions can be marked as processed in different ways. This is mostly a concern for the soft-bundle transactions. @@ -900,16 +880,28 @@ impl ConsensusAdapter { processed = epoch_store.consensus_messages_processed_notify(vec![transaction_key]) => { processed.expect("Storage error when waiting for consensus message processed"); self.metrics.sequencing_certificate_processed.with_label_values(&["consensus"]).inc(); + return ProcessedMethod::Consensus; }, processed = epoch_store.transactions_executed_in_checkpoint_notify(transaction_digests), if !transaction_digests.is_empty() => { processed.expect("Storage error when waiting for transaction executed in checkpoint"); self.metrics.sequencing_certificate_processed.with_label_values(&["checkpoint"]).inc(); } + processed = checkpoint_synced_future => { + processed.expect("Error when waiting for checkpoint sequence number"); + self.metrics.sequencing_certificate_processed.with_label_values(&["synced_checkpoint"]).inc(); + } } + ProcessedMethod::Checkpoint }); } - notifications.collect::>().await; + let processed_methods = notifications.collect::>().await; + for method in processed_methods { + if method == ProcessedMethod::Checkpoint { + return ProcessedMethod::Checkpoint; + } + } + ProcessedMethod::Consensus } } @@ -1035,6 +1027,13 @@ struct InflightDropGuard<'a> { positions_moved: Option, preceding_disconnected: Option, tx_type: &'static str, + processed_method: ProcessedMethod, +} + +#[derive(PartialEq, Eq)] +enum ProcessedMethod { + Consensus, + Checkpoint, } impl<'a> InflightDropGuard<'a> { @@ -1059,6 +1058,7 @@ impl<'a> InflightDropGuard<'a> { positions_moved: None, preceding_disconnected: None, tx_type, + processed_method: ProcessedMethod::Consensus, } } } @@ -1099,10 +1099,14 @@ impl<'a> Drop for InflightDropGuard<'a> { }; let latency = self.start.elapsed(); + let processed_method = match self.processed_method { + ProcessedMethod::Consensus => "processed_via_consensus", + ProcessedMethod::Checkpoint => "processed_via_checkpoint", + }; self.adapter .metrics .sequencing_certificate_latency - .with_label_values(&[&position, self.tx_type]) + .with_label_values(&[&position, self.tx_type, processed_method]) .observe(latency.as_secs_f64()); // Only sample latency after consensus quorum is up. Otherwise, the wait for consensus @@ -1116,7 +1120,8 @@ impl<'a> Drop for InflightDropGuard<'a> { self.tx_type, "shared_certificate" | "owned_certificate" | "checkpoint_signature" | "soft_bundle" ); - if sampled { + // if tx has been processed by checkpoint state sync, then exclude from the latency calculations as this can introduce to misleading results. + if sampled && self.processed_method == ProcessedMethod::Consensus { self.adapter.latency_observer.report(latency); } } diff --git a/crates/sui-core/src/consensus_handler.rs b/crates/sui-core/src/consensus_handler.rs index bc407592631db..d00b8273f1d02 100644 --- a/crates/sui-core/src/consensus_handler.rs +++ b/crates/sui-core/src/consensus_handler.rs @@ -10,9 +10,13 @@ use std::{ use arc_swap::ArcSwap; use consensus_config::Committee as ConsensusCommittee; -use consensus_core::CommitConsumerMonitor; +use consensus_core::{CommitConsumerMonitor, TransactionIndex, VerifiedBlock}; use lru::LruCache; -use mysten_metrics::{monitored_mpsc::UnboundedReceiver, monitored_scope, spawn_monitored_task}; +use mysten_metrics::{ + monitored_future, + monitored_mpsc::{self, UnboundedReceiver}, + monitored_scope, spawn_monitored_task, +}; use serde::{Deserialize, Serialize}; use sui_macros::{fail_point_async, fail_point_if}; use sui_protocol_config::ProtocolConfig; @@ -21,10 +25,13 @@ use sui_types::{ base_types::{AuthorityName, EpochId, ObjectID, SequenceNumber, TransactionDigest}, digests::ConsensusCommitDigest, executable_transaction::{TrustedExecutableTransaction, VerifiedExecutableTransaction}, - messages_consensus::{ConsensusTransaction, ConsensusTransactionKey, ConsensusTransactionKind}, + messages_consensus::{ + AuthorityIndex, ConsensusTransaction, ConsensusTransactionKey, ConsensusTransactionKind, + }, sui_system_state::epoch_start_sui_system_state::EpochStartSystemStateTrait, transaction::{SenderSignedData, VerifiedTransaction}, }; +use tokio::task::JoinSet; use tracing::{debug, error, info, instrument, trace_span, warn}; use crate::{ @@ -38,7 +45,9 @@ use crate::{ }, checkpoints::{CheckpointService, CheckpointServiceNotify}, consensus_throughput_calculator::ConsensusThroughputCalculator, - consensus_types::{consensus_output_api::ConsensusOutputAPI, AuthorityIndex}, + consensus_types::consensus_output_api::{ + parse_block_transactions, ConsensusCommitAPI, ParsedTransaction, + }, execution_cache::ObjectCacheRead, scoring_decision::update_low_scoring_authorities, transaction_manager::TransactionManager, @@ -69,7 +78,8 @@ impl ConsensusHandlerInitializer { } } - pub fn new_for_testing( + #[cfg(test)] + pub(crate) fn new_for_testing( state: Arc, checkpoint_service: Arc, ) -> Self { @@ -85,7 +95,7 @@ impl ConsensusHandlerInitializer { } } - pub fn new_consensus_handler(&self) -> ConsensusHandler { + pub(crate) fn new_consensus_handler(&self) -> ConsensusHandler { let new_epoch_start_state = self.epoch_store.epoch_start_state(); let consensus_committee = new_epoch_start_state.get_consensus_committee(); @@ -100,6 +110,10 @@ impl ConsensusHandlerInitializer { self.throughput_calculator.clone(), ) } + + pub(crate) fn metrics(&self) -> &Arc { + &self.state.metrics + } } pub struct ConsensusHandler { @@ -122,7 +136,8 @@ pub struct ConsensusHandler { metrics: Arc, /// Lru cache to quickly discard transactions processed by consensus processed_cache: LruCache, - transaction_scheduler: AsyncTransactionScheduler, + /// Enqueues transactions to the transaction manager via a separate task. + transaction_manager_sender: TransactionManagerSender, /// Using the throughput calculator to record the current consensus throughput throughput_calculator: Arc, } @@ -148,8 +163,8 @@ impl ConsensusHandler { if !last_consensus_stats.stats.is_initialized() { last_consensus_stats.stats = ConsensusStats::new(committee.size()); } - let transaction_scheduler = - AsyncTransactionScheduler::start(transaction_manager, epoch_store.clone()); + let transaction_manager_sender = + TransactionManagerSender::start(transaction_manager, epoch_store.clone()); Self { epoch_store, last_consensus_stats, @@ -159,31 +174,29 @@ impl ConsensusHandler { committee, metrics, processed_cache: LruCache::new(NonZeroUsize::new(PROCESSED_CACHE_CAP).unwrap()), - transaction_scheduler, + transaction_manager_sender, throughput_calculator, } } /// Returns the last subdag index processed by the handler. - pub fn last_processed_subdag_index(&self) -> u64 { + pub(crate) fn last_processed_subdag_index(&self) -> u64 { self.last_consensus_stats.index.sub_dag_index } + + pub(crate) fn transaction_manager_sender(&self) -> &TransactionManagerSender { + &self.transaction_manager_sender + } } impl ConsensusHandler { #[instrument(level = "debug", skip_all)] - async fn handle_consensus_output(&mut self, consensus_output: impl ConsensusOutputAPI) { + async fn handle_consensus_commit(&mut self, consensus_commit: impl ConsensusCommitAPI) { let _scope = monitored_scope("HandleConsensusOutput"); - // This code no longer supports old protocol versions. - assert!(self - .epoch_store - .protocol_config() - .consensus_order_end_of_epoch_last()); - let last_committed_round = self.last_consensus_stats.index.last_committed_round; - let round = consensus_output.leader_round(); + let round = consensus_commit.leader_round(); // TODO: Remove this once narwhal is deprecated. For now mysticeti will not return // more than one leader per round so we are not in danger of ignoring any commits. @@ -199,11 +212,11 @@ impl ConsensusHandler { return; } - /* (serialized, transaction, output_cert) */ + /* (transaction, serialized length) */ let mut transactions = vec![]; - let timestamp = consensus_output.commit_timestamp_ms(); - let leader_author = consensus_output.leader_author_index(); - let commit_sub_dag_index = consensus_output.commit_sub_dag_index(); + let timestamp = consensus_commit.commit_timestamp_ms(); + let leader_author = consensus_commit.leader_author_index(); + let commit_sub_dag_index = consensus_commit.commit_sub_dag_index(); let epoch_start = self .epoch_store @@ -219,7 +232,7 @@ impl ConsensusHandler { }; info!( - %consensus_output, + %consensus_commit, epoch = ?self.epoch_store.epoch(), "Received consensus output" ); @@ -269,7 +282,7 @@ impl ConsensusHandler { self.low_scoring_authorities.clone(), self.epoch_store.committee(), &self.committee, - consensus_output.reputation_score_sorted_desc(), + consensus_commit.reputation_score_sorted_desc(), &self.metrics, self.epoch_store .protocol_config() @@ -284,13 +297,18 @@ impl ConsensusHandler { { let span = trace_span!("process_consensus_certs"); let _guard = span.enter(); - for (authority_index, authority_transactions) in consensus_output.transactions() { + for (authority_index, parsed_transactions) in consensus_commit.transactions() { // TODO: consider only messages within 1~3 rounds of the leader? self.last_consensus_stats .stats .inc_num_messages(authority_index as usize); - for (transaction, serialized_len) in authority_transactions { - let kind = classify(&transaction); + for parsed in parsed_transactions { + // Skip executing rejected transactions. Unlocking is the responsibility of the + // consensus transaction handler. + if parsed.rejected { + continue; + } + let kind = classify(&parsed.transaction); self.metrics .consensus_handler_processed .with_label_values(&[kind]) @@ -298,22 +316,25 @@ impl ConsensusHandler { self.metrics .consensus_handler_transaction_sizes .with_label_values(&[kind]) - .observe(serialized_len as f64); + .observe(parsed.serialized_len as f64); + // UserTransaction exists only when mysticeti_fastpath is enabled in protocol config. if matches!( - &transaction.kind, + &parsed.transaction.kind, ConsensusTransactionKind::CertifiedTransaction(_) + | ConsensusTransactionKind::UserTransaction(_) ) { self.last_consensus_stats .stats .inc_num_user_transactions(authority_index as usize); } if let ConsensusTransactionKind::RandomnessStateUpdate(randomness_round, _) = - &transaction.kind + &parsed.transaction.kind { // These are deprecated and we should never see them. Log an error and eat the tx if one appears. error!("BUG: saw deprecated RandomnessStateUpdate tx for commit round {round:?}, randomness round {randomness_round:?}") } else { - let transaction = SequencedConsensusTransactionKind::External(transaction); + let transaction = + SequencedConsensusTransactionKind::External(parsed.transaction); transactions.push((transaction, authority_index)); } } @@ -383,14 +404,14 @@ impl ConsensusHandler { } } - let transactions_to_schedule = self + let executable_transactions = self .epoch_store .process_consensus_transactions_and_commit_boundary( all_transactions, &self.last_consensus_stats, &self.checkpoint_service, self.cache_reader.as_ref(), - &ConsensusCommitInfo::new(self.epoch_store.protocol_config(), &consensus_output), + &ConsensusCommitInfo::new(self.epoch_store.protocol_config(), &consensus_commit), &self.metrics, ) .await @@ -398,7 +419,7 @@ impl ConsensusHandler { // update the calculated throughput self.throughput_calculator - .add_transactions(timestamp, transactions_to_schedule.len() as u64); + .add_transactions(timestamp, executable_transactions.len() as u64); fail_point_if!("correlated-crash-after-consensus-commit-boundary", || { let key = [commit_sub_dag_index, self.epoch_store.epoch()]; @@ -409,32 +430,35 @@ impl ConsensusHandler { fail_point_async!("crash"); // for tests that produce random crashes - self.transaction_scheduler - .schedule(transactions_to_schedule) - .await; + self.transaction_manager_sender + .send(executable_transactions); } } -struct AsyncTransactionScheduler { - sender: tokio::sync::mpsc::Sender>, +/// Sends transactions to the transaction manager in a separate task, +/// to avoid blocking consensus handler. +#[derive(Clone)] +pub(crate) struct TransactionManagerSender { + // Using unbounded channel to avoid blocking consensus commit and transaction handler. + sender: monitored_mpsc::UnboundedSender>, } -impl AsyncTransactionScheduler { - pub fn start( +impl TransactionManagerSender { + fn start( transaction_manager: Arc, epoch_store: Arc, ) -> Self { - let (sender, recv) = tokio::sync::mpsc::channel(16); + let (sender, recv) = monitored_mpsc::unbounded_channel("transaction_manager_sender"); spawn_monitored_task!(Self::run(recv, transaction_manager, epoch_store)); Self { sender } } - pub async fn schedule(&self, transactions: Vec) { - self.sender.send(transactions).await.ok(); + fn send(&self, transactions: Vec) { + let _ = self.sender.send(transactions); } - pub async fn run( - mut recv: tokio::sync::mpsc::Receiver>, + async fn run( + mut recv: monitored_mpsc::UnboundedReceiver>, transaction_manager: Arc, epoch_store: Arc, ) { @@ -445,48 +469,51 @@ impl AsyncTransactionScheduler { } } -/// Consensus handler used by Mysticeti. Since Mysticeti repo is not yet integrated, we use a -/// channel to receive the consensus output from Mysticeti. -/// During initialization, the sender is passed into Mysticeti which can send consensus output -/// to the channel. -pub struct MysticetiConsensusHandler { - handle: Option>, +/// Manages the lifetime of tasks handling the commits and transactions output by consensus. +pub(crate) struct MysticetiConsensusHandler { + tasks: JoinSet<()>, } impl MysticetiConsensusHandler { - pub fn new( + pub(crate) fn new( mut consensus_handler: ConsensusHandler, - mut receiver: UnboundedReceiver, + consensus_transaction_handler: ConsensusTransactionHandler, + mut commit_receiver: UnboundedReceiver, + mut transaction_receiver: UnboundedReceiver)>>, commit_consumer_monitor: Arc, ) -> Self { - let handle = spawn_monitored_task!(async move { + let mut tasks = JoinSet::new(); + tasks.spawn(monitored_future!(async move { // TODO: pause when execution is overloaded, so consensus can detect the backpressure. - while let Some(consensus_output) = receiver.recv().await { - let commit_index = consensus_output.commit_ref.index; + while let Some(consensus_commit) = commit_receiver.recv().await { + let commit_index = consensus_commit.commit_ref.index; consensus_handler - .handle_consensus_output(consensus_output) + .handle_consensus_commit(consensus_commit) .await; commit_consumer_monitor.set_highest_handled_commit(commit_index); } - }); - Self { - handle: Some(handle), - } - } - - pub async fn abort(&mut self) { - if let Some(handle) = self.handle.take() { - handle.abort(); - let _ = handle.await; + })); + if consensus_transaction_handler.enabled() { + tasks.spawn(monitored_future!(async move { + while let Some(blocks_and_rejected_transactions) = transaction_receiver.recv().await + { + let parsed_transactions = blocks_and_rejected_transactions + .into_iter() + .flat_map(|(block, rejected_transactions)| { + parse_block_transactions(&block, &rejected_transactions) + }) + .collect::>(); + consensus_transaction_handler + .handle_consensus_transactions(parsed_transactions) + .await; + } + })); } + Self { tasks } } -} -impl Drop for MysticetiConsensusHandler { - fn drop(&mut self) { - if let Some(handle) = self.handle.take() { - handle.abort(); - } + pub(crate) async fn abort(&mut self) { + self.tasks.shutdown().await; } } @@ -749,11 +776,11 @@ pub struct ConsensusCommitInfo { } impl ConsensusCommitInfo { - fn new(protocol_config: &ProtocolConfig, consensus_output: &impl ConsensusOutputAPI) -> Self { + fn new(protocol_config: &ProtocolConfig, consensus_commit: &impl ConsensusCommitAPI) -> Self { Self { - round: consensus_output.leader_round(), - timestamp: consensus_output.commit_timestamp_ms(), - consensus_commit_digest: consensus_output.consensus_digest(protocol_config), + round: consensus_commit.leader_round(), + timestamp: consensus_commit.commit_timestamp_ms(), + consensus_commit_digest: consensus_commit.consensus_digest(protocol_config), #[cfg(any(test, feature = "test-utils"))] skip_consensus_commit_prologue_in_test: false, @@ -829,6 +856,105 @@ impl ConsensusCommitInfo { } } +/// Handles certified and rejected transactions output by consensus. +pub(crate) struct ConsensusTransactionHandler { + /// Whether to enable handling certified transactions. + enabled: bool, + /// Per-epoch store. + epoch_store: Arc, + /// Enqueues transactions to the transaction manager via a separate task. + transaction_manager_sender: TransactionManagerSender, + /// Metrics for consensus transaction handling. + metrics: Arc, +} + +impl ConsensusTransactionHandler { + pub fn new( + epoch_store: Arc, + transaction_manager_sender: TransactionManagerSender, + metrics: Arc, + ) -> Self { + Self { + enabled: epoch_store.protocol_config().mysticeti_fastpath(), + epoch_store, + transaction_manager_sender, + metrics, + } + } + + pub fn enabled(&self) -> bool { + self.enabled + } + + pub async fn handle_consensus_transactions(&self, parsed_transactions: Vec) { + let mut pending_consensus_transactions = vec![]; + let executable_transactions: Vec<_> = parsed_transactions + .into_iter() + .filter_map(|parsed| { + // TODO(fastpath): unlock rejected transactions. + // TODO(fastpath): maybe avoid parsing blocks twice between commit and transaction handling? + if parsed.rejected { + self.metrics + .consensus_transaction_handler_processed + .with_label_values(&["rejected"]) + .inc(); + return None; + } + self.metrics + .consensus_transaction_handler_processed + .with_label_values(&["certified"]) + .inc(); + match &parsed.transaction.kind { + ConsensusTransactionKind::UserTransaction(tx) => { + // TODO(fastpath): use a separate function to check if a transaction should be executed in fastpath. + if tx.contains_shared_object() { + return None; + } + pending_consensus_transactions.push(parsed.transaction.clone()); + let tx = VerifiedTransaction::new_from_verified(*tx.clone()); + Some(VerifiedExecutableTransaction::new_from_consensus( + tx, + self.epoch_store.epoch(), + parsed.round, + parsed.authority, + parsed.transaction_index, + )) + } + _ => None, + } + }) + .collect(); + + if pending_consensus_transactions.is_empty() { + return; + } + { + let reconfig_state = self.epoch_store.get_reconfig_state_read_lock_guard(); + // Stop executing fastpath transactions when epoch change starts. + if !reconfig_state.should_accept_user_certs() { + return; + } + // Otherwise, try to ensure the certified transactions get into consensus before epoch change. + // TODO(fastpath): avoid race with removals in consensus adapter, by waiting to handle commit after + // all blocks in the commit are processed via the transaction handler. Other kinds of races need to be + // avoided as well. Or we can track pending consensus transactions inside consensus instead. + self.epoch_store + .insert_pending_consensus_transactions( + &pending_consensus_transactions, + Some(&reconfig_state), + ) + .unwrap_or_else(|e| { + panic!("Failed to insert pending consensus transactions: {}", e) + }); + } + self.metrics + .consensus_transaction_handler_fastpath_executions + .inc_by(executable_transactions.len() as u64); + self.transaction_manager_sender + .send(executable_transactions); + } +} + #[cfg(test)] mod tests { use consensus_core::{ @@ -839,6 +965,7 @@ mod tests { use sui_types::{ base_types::{random_object_ref, AuthorityName, SuiAddress}, committee::Committee, + crypto::deterministic_random_account_key, messages_consensus::{ AuthorityCapabilitiesV1, ConsensusTransaction, ConsensusTransactionKind, }, @@ -856,12 +983,14 @@ mod tests { test_authority_builder::TestAuthorityBuilder, }, checkpoints::CheckpointServiceNoop, - consensus_adapter::consensus_tests::{test_certificates, test_gas_objects}, + consensus_adapter::consensus_tests::{ + test_certificates, test_gas_objects, test_user_transaction, + }, post_consensus_tx_reorder::PostConsensusTxReorder, }; #[tokio::test] - pub async fn test_consensus_handler() { + pub async fn test_consensus_commit_handler() { // GIVEN let mut objects = test_gas_objects(); let shared_object = Object::shared_for_testing(); @@ -922,6 +1051,7 @@ mod tests { let committed_sub_dag = CommittedSubDag::new( leader_block.reference(), blocks.clone(), + vec![vec![]; blocks.len()], leader_block.timestamp_ms(), CommitRef::new(10, CommitDigest::MIN), vec![], @@ -929,7 +1059,7 @@ mod tests { // AND processing the consensus output once consensus_handler - .handle_consensus_output(committed_sub_dag.clone()) + .handle_consensus_commit(committed_sub_dag.clone()) .await; // AND capturing the consensus stats @@ -956,13 +1086,139 @@ mod tests { // THEN the consensus stats do not update for _ in 0..2 { consensus_handler - .handle_consensus_output(committed_sub_dag.clone()) + .handle_consensus_commit(committed_sub_dag.clone()) .await; let last_consensus_stats_2 = consensus_handler.last_consensus_stats.clone(); assert_eq!(last_consensus_stats_1, last_consensus_stats_2); } } + #[tokio::test] + pub async fn test_consensus_transaction_handler() { + // GIVEN + // 1 account keypair + let (sender, keypair) = deterministic_random_account_key(); + // 8 gas objects. + let gas_objects: Vec = (0..8) + .map(|_| Object::with_id_owner_for_testing(ObjectID::random(), sender)) + .collect(); + // 4 owned objects. + let owned_objects: Vec = (0..4) + .map(|_| Object::with_id_owner_for_testing(ObjectID::random(), sender)) + .collect(); + // 4 shared objects. + let shared_objects: Vec = (0..4) + .map(|_| Object::shared_for_testing()) + .collect::>(); + let mut all_objects = gas_objects.clone(); + all_objects.extend(owned_objects.clone()); + all_objects.extend(shared_objects.clone()); + + let network_config = + sui_swarm_config::network_config_builder::ConfigBuilder::new_with_temp_dir() + .with_objects(all_objects.clone()) + .build(); + + let state = TestAuthorityBuilder::new() + .with_network_config(&network_config, 0) + .build() + .await; + let epoch_store = state.epoch_store_for_testing().clone(); + let transaction_manager_sender = TransactionManagerSender::start( + state.transaction_manager().clone(), + epoch_store.clone(), + ); + let transaction_handler = ConsensusTransactionHandler::new( + epoch_store, + transaction_manager_sender, + state.metrics.clone(), + ); + + // AND create test transactions alternating between owned and shared input. + let mut transactions = vec![]; + for (i, gas_object) in gas_objects.iter().enumerate() { + let input_object = if i % 2 == 0 { + owned_objects.get(i / 2).unwrap().clone() + } else { + shared_objects.get(i / 2).unwrap().clone() + }; + let transaction = test_user_transaction( + &state, + sender, + &keypair, + gas_object.clone(), + vec![input_object], + ) + .await; + transactions.push(transaction); + } + + let serialized_transactions: Vec<_> = transactions + .iter() + .map(|t| { + Transaction::new( + bcs::to_bytes(&ConsensusTransaction::new_user_transaction_message( + &state.name, + t.inner().clone(), + )) + .unwrap(), + ) + }) + .collect(); + + // AND create block for all transactions + let block = VerifiedBlock::new_for_test( + TestBlock::new(100, 1) + .set_transactions(serialized_transactions.clone()) + .build(), + ); + + // AND set rejected transactions. + let rejected_transactions = vec![0, 3, 4]; + + // AND process the transactions from consensus output. + transaction_handler + .handle_consensus_transactions(parse_block_transactions(&block, &rejected_transactions)) + .await; + + // THEN check for execution status of transactions. + for (i, t) in transactions.iter().enumerate() { + // Do not expect shared transactions or rejected transactions to be executed. + if i % 2 == 1 || rejected_transactions.contains(&(i as TransactionIndex)) { + continue; + } + let digest = t.digest(); + if let Ok(Ok(_)) = tokio::time::timeout( + std::time::Duration::from_secs(10), + state.notify_read_effects(*digest), + ) + .await + { + // Effects exist as expected. + } else { + panic!("Transaction {} {} did not execute", i, digest); + } + } + + // THEN check for no inflight or suspended transactions. + state.transaction_manager().check_empty_for_testing(); + + // THEN check that rejected transactions are not executed. + for (i, t) in transactions.iter().enumerate() { + // Expect shared transactions or rejected transactions to not have executed. + if i % 2 == 0 && !rejected_transactions.contains(&(i as TransactionIndex)) { + continue; + } + let digest = t.digest(); + assert!( + !state.is_tx_already_executed(digest).unwrap(), + "Rejected transaction {} {} should not have been executed", + i, + digest + ); + } + } + #[test] fn test_order_by_gas_price() { let mut v = vec![cap_txn(10), user_txn(42), user_txn(100), cap_txn(1)]; diff --git a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs index 93612791e90f3..cfee957039a0d 100644 --- a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs +++ b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use consensus_config::{Committee, NetworkKeyPair, Parameters, ProtocolKeyPair}; use consensus_core::{CommitConsumer, CommitIndex, ConsensusAuthority}; use fastcrypto::ed25519; -use mysten_metrics::{monitored_mpsc::unbounded_channel, RegistryID, RegistryService}; +use mysten_metrics::{RegistryID, RegistryService}; use prometheus::Registry; use sui_config::NodeConfig; use sui_protocol_config::ConsensusNetwork; @@ -19,7 +19,9 @@ use tracing::info; use crate::{ authority::authority_per_epoch_store::AuthorityPerEpochStore, - consensus_handler::{ConsensusHandlerInitializer, MysticetiConsensusHandler}, + consensus_handler::{ + ConsensusHandlerInitializer, ConsensusTransactionHandler, MysticetiConsensusHandler, + }, consensus_manager::{ ConsensusManagerMetrics, ConsensusManagerTrait, Running, RunningLockGuard, }, @@ -144,17 +146,11 @@ impl ConsensusManagerTrait for MysticetiManager { let registry = Registry::new_custom(Some("consensus".to_string()), None).unwrap(); - let (commit_sender, commit_receiver) = unbounded_channel("consensus_output"); - let consensus_handler = consensus_handler_initializer.new_consensus_handler(); - let consumer = CommitConsumer::new( - commit_sender, - consensus_handler.last_processed_subdag_index() as CommitIndex, - ); - let monitor = consumer.monitor(); + let (commit_consumer, commit_receiver, transaction_receiver) = + CommitConsumer::new(consensus_handler.last_processed_subdag_index() as CommitIndex); + let monitor = commit_consumer.monitor(); - // TODO(mysticeti): Investigate if we need to return potential errors from - // AuthorityNode and add retries here? let boot_counter = *self.boot_counter.lock().await; let authority = ConsensusAuthority::start( network_type, @@ -165,7 +161,7 @@ impl ConsensusManagerTrait for MysticetiManager { self.protocol_keypair.clone(), self.network_keypair.clone(), Arc::new(tx_validator.clone()), - consumer, + commit_consumer, registry.clone(), boot_counter, ) @@ -185,7 +181,18 @@ impl ConsensusManagerTrait for MysticetiManager { self.client.set(client); // spin up the new mysticeti consensus handler to listen for committed sub dags - let handler = MysticetiConsensusHandler::new(consensus_handler, commit_receiver, monitor); + let consensus_transaction_handler = ConsensusTransactionHandler::new( + epoch_store.clone(), + consensus_handler.transaction_manager_sender().clone(), + consensus_handler_initializer.metrics().clone(), + ); + let handler = MysticetiConsensusHandler::new( + consensus_handler, + consensus_transaction_handler, + commit_receiver, + transaction_receiver, + monitor, + ); let mut consensus_handler = self.consensus_handler.lock().await; *consensus_handler = Some(handler); diff --git a/crates/sui-core/src/consensus_types/consensus_output_api.rs b/crates/sui-core/src/consensus_types/consensus_output_api.rs index 938bfbaa4546d..e0c2eeebc16e4 100644 --- a/crates/sui-core/src/consensus_types/consensus_output_api.rs +++ b/crates/sui-core/src/consensus_types/consensus_output_api.rs @@ -1,19 +1,30 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::fmt::Display; +use std::{cmp::Ordering, fmt::Display}; -use consensus_core::{BlockAPI, CommitDigest}; +use consensus_core::{BlockAPI, CommitDigest, TransactionIndex, VerifiedBlock}; use sui_protocol_config::ProtocolConfig; -use sui_types::{digests::ConsensusCommitDigest, messages_consensus::ConsensusTransaction}; +use sui_types::{ + digests::ConsensusCommitDigest, + messages_consensus::{AuthorityIndex, ConsensusTransaction, Round}, +}; -use crate::consensus_types::AuthorityIndex; - -/// A list of tuples of: -/// (block origin authority index, all transactions contained in the block). -/// For each transaction, returns deserialized transaction and its serialized size. -type ConsensusOutputTransactions = Vec<(AuthorityIndex, Vec<(ConsensusTransaction, usize)>)>; +pub(crate) struct ParsedTransaction { + // Transaction from consensus output. + pub(crate) transaction: ConsensusTransaction, + // Whether the transaction was rejected in voting. + pub(crate) rejected: bool, + // Bytes length of the serialized transaction + pub(crate) serialized_len: usize, + // Consensus round of the block containing the transaction. + pub(crate) round: Round, + // Authority index of the block containing the transaction. + pub(crate) authority: AuthorityIndex, + // Transaction index in the block. + pub(crate) transaction_index: TransactionIndex, +} -pub(crate) trait ConsensusOutputAPI: Display { +pub(crate) trait ConsensusCommitAPI: Display { fn reputation_score_sorted_desc(&self) -> Option>; fn leader_round(&self) -> u64; fn leader_author_index(&self) -> AuthorityIndex; @@ -24,14 +35,14 @@ pub(crate) trait ConsensusOutputAPI: Display { /// Returns a unique global index for each committed sub-dag. fn commit_sub_dag_index(&self) -> u64; - /// Returns all transactions in the commit. - fn transactions(&self) -> ConsensusOutputTransactions; + /// Returns all accepted and rejected transactions per block in the commit in deterministic order. + fn transactions(&self) -> Vec<(AuthorityIndex, Vec)>; /// Returns the digest of consensus output. fn consensus_digest(&self, protocol_config: &ProtocolConfig) -> ConsensusCommitDigest; } -impl ConsensusOutputAPI for consensus_core::CommittedSubDag { +impl ConsensusCommitAPI for consensus_core::CommittedSubDag { fn reputation_score_sorted_desc(&self) -> Option> { if !self.reputation_scores_desc.is_empty() { Some( @@ -62,30 +73,15 @@ impl ConsensusOutputAPI for consensus_core::CommittedSubDag { self.commit_ref.index.into() } - fn transactions(&self) -> ConsensusOutputTransactions { + fn transactions(&self) -> Vec<(AuthorityIndex, Vec)> { self.blocks .iter() - .map(|block| { - let round = block.round(); - let author = block.author().value() as AuthorityIndex; - let transactions: Vec<_> = block - .transactions() - .iter() - .flat_map(|tx| { - let transaction = bcs::from_bytes::(tx.data()); - match transaction { - Ok(transaction) => Some(( - transaction, - tx.data().len(), - )), - Err(err) => { - tracing::error!("Failed to deserialize sequenced consensus transaction(this should not happen) {} from {author} at {round}", err); - None - }, - } - }) - .collect(); - (author, transactions) + .zip(self.rejected_transactions_by_block.iter()) + .map(|(block, rejected_transactions)| { + ( + block.author().value() as AuthorityIndex, + parse_block_transactions(block, rejected_transactions), + ) }) .collect() } @@ -101,3 +97,49 @@ impl ConsensusOutputAPI for consensus_core::CommittedSubDag { } } } + +pub(crate) fn parse_block_transactions( + block: &VerifiedBlock, + rejected_transactions: &[TransactionIndex], +) -> Vec { + let round = block.round(); + let authority = block.author().value() as AuthorityIndex; + + let mut rejected_idx = 0; + block + .transactions() + .iter().enumerate() + .map(|(index, tx)| { + let transaction = match bcs::from_bytes::(tx.data()) { + Ok(transaction) => transaction, + Err(err) => { + panic!("Failed to deserialize sequenced consensus transaction(this should not happen) {err} from {authority} at {round}"); + }, + }; + let rejected = if rejected_idx < rejected_transactions.len() { + match (index as TransactionIndex).cmp(&rejected_transactions[rejected_idx]) { + Ordering::Less => { + false + }, + Ordering::Equal => { + rejected_idx += 1; + true + }, + Ordering::Greater => { + panic!("Rejected transaction indices are not in order. Block {block:?}, rejected transactions: {rejected_transactions:?}"); + }, + } + } else { + false + }; + ParsedTransaction { + transaction, + rejected, + serialized_len: tx.data().len(), + round, + authority, + transaction_index: index as TransactionIndex, + } + }) + .collect() +} diff --git a/crates/sui-core/src/consensus_types/mod.rs b/crates/sui-core/src/consensus_types/mod.rs index 5aab7d258d1bd..742bb95d2a16b 100644 --- a/crates/sui-core/src/consensus_types/mod.rs +++ b/crates/sui-core/src/consensus_types/mod.rs @@ -2,7 +2,3 @@ // SPDX-License-Identifier: Apache-2.0 pub(crate) mod consensus_output_api; - -/// An unique integer ID for a validator used by consensus. -/// In Mysticeti, this is used the same way as the AuthorityIndex type there. -pub type AuthorityIndex = u32; diff --git a/crates/sui-core/src/consensus_validator.rs b/crates/sui-core/src/consensus_validator.rs index 9c2609e36d971..ad47a537b2110 100644 --- a/crates/sui-core/src/consensus_validator.rs +++ b/crates/sui-core/src/consensus_validator.rs @@ -91,7 +91,13 @@ impl SuiTxValidator { | ConsensusTransactionKind::RandomnessStateUpdate(_, _) => {} ConsensusTransactionKind::UserTransaction(_tx) => { - // TODO: implement verification for uncertified user transactions if needed + if !self.epoch_store.protocol_config().mysticeti_fastpath() { + return Err(SuiError::UnexpectedMessage( + "ConsensusTransactionKind::UserTransaction is unsupported".to_string(), + ) + .into()); + } + // TODO(fastpath): implement verification for uncertified user transactions. } } } diff --git a/crates/sui-core/src/execution_cache/writeback_cache.rs b/crates/sui-core/src/execution_cache/writeback_cache.rs index 0d157b476ace8..8431429b683db 100644 --- a/crates/sui-core/src/execution_cache/writeback_cache.rs +++ b/crates/sui-core/src/execution_cache/writeback_cache.rs @@ -460,7 +460,7 @@ impl WritebackCache { version: SequenceNumber, object: ObjectEntry, ) { - debug!(?object_id, ?version, ?object, "inserting object entry"); + trace!(?object_id, ?version, ?object, "inserting object entry"); fail_point_async!("write_object_entry"); self.metrics.record_cache_write("object"); self.dirty diff --git a/crates/sui-core/src/execution_driver.rs b/crates/sui-core/src/execution_driver.rs index d43ee31adf129..ca433a8a4d383 100644 --- a/crates/sui-core/src/execution_driver.rs +++ b/crates/sui-core/src/execution_driver.rs @@ -107,7 +107,8 @@ pub async fn execution_process( authority.metrics.execution_rate_tracker.lock().record(); // Certificate execution can take significant time, so run it in a separate task. - spawn_monitored_task!(async move { + let epoch_store_clone = epoch_store.clone(); + spawn_monitored_task!(epoch_store.within_alive_epoch(async move { let _scope = monitored_scope("ExecutionDriver::task"); let _guard = permit; if let Ok(true) = authority.is_tx_already_executed(&digest) { @@ -118,7 +119,7 @@ pub async fn execution_process( fail_point_async!("transaction_execution_delay"); attempts += 1; let res = authority - .try_execute_immediately(&certificate, expected_effects_digest, &epoch_store) + .try_execute_immediately(&certificate, expected_effects_digest, &epoch_store_clone) .await; if let Err(e) = res { if attempts == EXECUTION_MAX_ATTEMPTS { @@ -136,6 +137,6 @@ pub async fn execution_process( .metrics .execution_driver_executed_transactions .inc(); - }.instrument(error_span!("execution_driver", tx_digest = ?digest))); + }.instrument(error_span!("execution_driver", tx_digest = ?digest)))); } } diff --git a/crates/sui-core/src/scoring_decision.rs b/crates/sui-core/src/scoring_decision.rs index 486da40c786bc..fd48e5bfddccb 100644 --- a/crates/sui-core/src/scoring_decision.rs +++ b/crates/sui-core/src/scoring_decision.rs @@ -4,10 +4,12 @@ use std::{collections::HashMap, sync::Arc}; use arc_swap::ArcSwap; use consensus_config::Committee as ConsensusCommittee; -use sui_types::{base_types::AuthorityName, committee::Committee}; +use sui_types::{ + base_types::AuthorityName, committee::Committee, messages_consensus::AuthorityIndex, +}; use tracing::debug; -use crate::{authority::AuthorityMetrics, consensus_types::AuthorityIndex}; +use crate::authority::AuthorityMetrics; /// Updates list of authorities that are deemed to have low reputation scores by consensus /// these may be lagging behind the network, byzantine, or not reliably participating for any reason. diff --git a/crates/sui-core/src/transaction_manager.rs b/crates/sui-core/src/transaction_manager.rs index 368e259c2dbcd..0ffd0c7a92b51 100644 --- a/crates/sui-core/src/transaction_manager.rs +++ b/crates/sui-core/src/transaction_manager.rs @@ -863,7 +863,7 @@ impl TransactionManager { // Verify TM has no pending item for tests. #[cfg(test)] - fn check_empty_for_testing(&self) { + pub(crate) fn check_empty_for_testing(&self) { let reconfig_lock = self.inner.read(); let inner = reconfig_lock.read(); assert!( diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 36ebd6f737292..9dd2af551bff3 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -38,7 +38,8 @@ use sui_types::quorum_driver_types::{ QuorumDriverError, QuorumDriverResponse, QuorumDriverResult, }; use sui_types::sui_system_state::SuiSystemState; -use sui_types::transaction::VerifiedTransaction; +use sui_types::transaction::{TransactionData, VerifiedTransaction}; +use sui_types::transaction_executor::SimulateTransactionResult; use tokio::sync::broadcast::error::RecvError; use tokio::sync::broadcast::Receiver; use tokio::task::JoinHandle; @@ -710,4 +711,11 @@ where ) -> Result { self.execute_transaction_v3(request, client_addr).await } + + fn simulate_transaction( + &self, + transaction: TransactionData, + ) -> Result { + self.validator_state.simulate_transaction(transaction) + } } diff --git a/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs b/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs index cc3ffa82fb08e..da73141e0eabe 100644 --- a/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs @@ -504,11 +504,7 @@ async fn test_map_reducer() { |mut accumulated_state, authority_name, _authority_weight, _result| { Box::pin(async move { accumulated_state.insert(authority_name); - if accumulated_state.len() <= 3 { - ReduceOutput::Continue(accumulated_state) - } else { - ReduceOutput::ContinueWithTimeout(accumulated_state, Duration::from_millis(10)) - } + ReduceOutput::Continue(accumulated_state) }) }, // large delay @@ -1355,6 +1351,50 @@ async fn test_handle_transaction_response() { ) .await; + println!("Case 8.3 - Retryable Transaction (EpochEnded Error)"); + + set_tx_info_response_with_signed_tx(&mut clients, &authority_keys, &tx, 0); + + // 2 out 4 validators return epoch ended error + for (name, _) in authority_keys.iter().skip(2) { + clients + .get_mut(name) + .unwrap() + .set_tx_info_response_error(SuiError::EpochEnded(0)); + } + let agg = get_genesis_agg(authorities.clone(), clients.clone()); + assert_resp_err( + &agg, + tx.clone().into(), + |e| { + matches!( + e, + AggregatorProcessTransactionError::RetryableTransaction { .. } + ) + }, + |e| matches!(e, SuiError::EpochEnded(0)), + ) + .await; + + println!("Case 8.4 - Retryable Transaction (EpochEnded Error) eventually succeeds"); + + set_tx_info_response_with_signed_tx(&mut clients, &authority_keys, &tx, 0); + + // 1 out 4 validators return epoch ended error + for (name, _) in authority_keys.iter().take(1) { + clients + .get_mut(name) + .unwrap() + .set_tx_info_response_error(SuiError::EpochEnded(0)); + } + + let agg = get_genesis_agg(authorities.clone(), clients.clone()); + let cert = agg + .process_transaction(tx.clone().into(), Some(client_ip)) + .await + .unwrap(); + matches!(cert, ProcessTransactionResult::Certified { .. }); + println!("Case 9 - Non-Retryable Transaction (>=2f+1 ObjectNotFound Error)"); // >= 2f+1 object not found errors set_retryable_tx_info_response_error(&mut clients, &authority_keys); diff --git a/crates/sui-core/src/unit_tests/authority_tests.rs b/crates/sui-core/src/unit_tests/authority_tests.rs index 71b406679101a..70ec3e454c5de 100644 --- a/crates/sui-core/src/unit_tests/authority_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_tests.rs @@ -5866,6 +5866,7 @@ async fn test_consensus_handler_per_object_congestion_control( PerObjectCongestionControlMode::None => unreachable!(), PerObjectCongestionControlMode::TotalGasBudget => 5, PerObjectCongestionControlMode::TotalTxCount => 2, + PerObjectCongestionControlMode::TotalGasBudgetWithCap => 5, }; let gas_objects_commit_1 = create_gas_objects(5 + non_congested_tx_count, sender); let gas_objects_commit_2 = create_gas_objects(non_congested_tx_count, sender); @@ -5891,6 +5892,15 @@ async fn test_consensus_handler_per_object_congestion_control( protocol_config .set_max_accumulated_txn_cost_per_object_in_mysticeti_commit_for_testing(2); } + PerObjectCongestionControlMode::TotalGasBudgetWithCap => { + protocol_config + .set_max_accumulated_txn_cost_per_object_in_narwhal_commit_for_testing(200_000_000); + protocol_config + .set_max_accumulated_txn_cost_per_object_in_mysticeti_commit_for_testing( + 200_000_000, + ); + protocol_config.set_gas_budget_based_txn_cost_cap_factor_for_testing(100_000_000); + } } protocol_config.set_max_deferral_rounds_for_congestion_control_for_testing(1000); // Set to a large number so that we don't hit this limit. let authority = TestAuthorityBuilder::new() @@ -6082,6 +6092,14 @@ async fn test_consensus_handler_per_object_congestion_control_using_tx_count() { .await; } +#[sim_test] +async fn test_consensus_handler_per_object_congestion_control_using_budget_with_cap() { + test_consensus_handler_per_object_congestion_control( + PerObjectCongestionControlMode::TotalGasBudgetWithCap, + ) + .await; +} + // Tests congestion control triggered transaction cancellation in consensus handler: // 1. Consensus handler cancels transactions that are deferred for too many rounds. // 2. Shared locks for cancelled transaction are set correctly. diff --git a/crates/sui-core/src/unit_tests/congestion_control_tests.rs b/crates/sui-core/src/unit_tests/congestion_control_tests.rs index 4890b2006644b..24a6defc2b407 100644 --- a/crates/sui-core/src/unit_tests/congestion_control_tests.rs +++ b/crates/sui-core/src/unit_tests/congestion_control_tests.rs @@ -301,6 +301,7 @@ async fn test_congestion_control_execution_cancellation() { SharedObjectCongestionTracker::new_with_initial_value_for_test( &[(shared_object_1.0, 10)], PerObjectCongestionControlMode::TotalGasBudget, + Some(1000), // Not used. ), ) }); diff --git a/crates/sui-core/src/unit_tests/consensus_tests.rs b/crates/sui-core/src/unit_tests/consensus_tests.rs index 59516fe5f318f..46247ae04edf1 100644 --- a/crates/sui-core/src/unit_tests/consensus_tests.rs +++ b/crates/sui-core/src/unit_tests/consensus_tests.rs @@ -7,20 +7,27 @@ use super::*; use crate::authority::{authority_tests::init_state_with_objects, AuthorityState}; use crate::checkpoints::CheckpointServiceNoop; use crate::consensus_handler::SequencedConsensusTransaction; +use fastcrypto::traits::KeyPair; use move_core_types::{account_address::AccountAddress, ident_str}; use narwhal_types::Transactions; use narwhal_types::TransactionsServer; use narwhal_types::{Empty, TransactionProto}; +use rand::rngs::StdRng; +use rand::SeedableRng; use sui_network::tonic; -use sui_types::crypto::deterministic_random_account_key; +use sui_types::crypto::{deterministic_random_account_key, AccountKeyPair}; +use sui_types::gas::GasCostSummary; +use sui_types::messages_checkpoint::{ + CheckpointContents, CheckpointSignatureMessage, CheckpointSummary, SignedCheckpointSummary, +}; use sui_types::multiaddr::Multiaddr; use sui_types::transaction::TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS; -use sui_types::utils::to_sender_signed_transaction; +use sui_types::utils::{make_committee_key, to_sender_signed_transaction}; use sui_types::SUI_FRAMEWORK_PACKAGE_ID; use sui_types::{ - base_types::ObjectID, + base_types::{ExecutionDigests, ObjectID, SuiAddress}, object::Object, - transaction::{CallArg, CertifiedTransaction, ObjectArg, TransactionData}, + transaction::{CallArg, CertifiedTransaction, ObjectArg, TransactionData, VerifiedTransaction}, }; use tokio::sync::mpsc::channel; use tokio::sync::mpsc::{Receiver, Sender}; @@ -105,6 +112,70 @@ pub async fn test_certificates( certificates } +/// Fixture: creates a transaction using the specified gas and input objects. +pub async fn test_user_transaction( + authority: &AuthorityState, + sender: SuiAddress, + keypair: &AccountKeyPair, + gas_object: Object, + input_objects: Vec, +) -> VerifiedTransaction { + let epoch_store = authority.load_epoch_store_one_call_per_task(); + let rgp = epoch_store.reference_gas_price(); + + // Object digest may be different in genesis than originally generated. + let gas_object = authority + .get_object(&gas_object.id()) + .await + .unwrap() + .unwrap(); + let mut input_objs = vec![]; + for obj in input_objects { + input_objs.push(authority.get_object(&obj.id()).await.unwrap().unwrap()); + } + + let mut object_args: Vec<_> = input_objs + .into_iter() + .map(|obj| { + if obj.is_shared() { + ObjectArg::SharedObject { + id: obj.id(), + initial_shared_version: obj.version(), + mutable: true, + } + } else { + ObjectArg::ImmOrOwnedObject(obj.compute_object_reference()) + } + }) + .map(CallArg::Object) + .collect(); + object_args.extend(vec![ + CallArg::Pure(16u64.to_le_bytes().to_vec()), + CallArg::Pure(bcs::to_bytes(&AccountAddress::from(sender)).unwrap()), + ]); + + // Make a sample transaction. + let module = "object_basics"; + let function = "create"; + + let data = TransactionData::new_move_call( + sender, + SUI_FRAMEWORK_PACKAGE_ID, + ident_str!(module).to_owned(), + ident_str!(function).to_owned(), + /* type_args */ vec![], + gas_object.compute_object_reference(), + object_args, + rgp * TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS, + rgp, + ) + .unwrap(); + + epoch_store + .verify_transaction(to_sender_signed_transaction(data, keypair)) + .unwrap() +} + pub fn make_consensus_adapter_for_test( state: Arc, process_via_checkpoint: HashSet, @@ -155,6 +226,11 @@ pub fn make_consensus_adapter_for_test( .await?, ); } + } else if let SequencedConsensusTransactionKey::External( + ConsensusTransactionKey::CheckpointSignature(_, checkpoint_sequence_number), + ) = tx.transaction.key() + { + epoch_store.notify_synced_checkpoint(checkpoint_sequence_number); } else { transactions.extend( epoch_store @@ -264,6 +340,58 @@ async fn submit_multiple_transactions_to_consensus_adapter() { .into_iter() .map(|certificate| ConsensusTransaction::new_certificate_message(&state.name, certificate)) .collect::>(); + + let waiter = adapter + .submit_batch( + &transactions, + Some(&epoch_store.get_reconfig_state_read_lock_guard()), + &epoch_store, + ) + .unwrap(); + waiter.await.unwrap(); +} + +#[tokio::test] +async fn submit_checkpoint_signature_to_consensus_adapter() { + telemetry_subscribers::init_for_testing(); + + let mut rng = StdRng::seed_from_u64(1_100); + let (keys, committee) = make_committee_key(&mut rng); + + // Initialize an authority + let state = init_state_with_objects(vec![]).await; + let epoch_store = state.epoch_store_for_testing(); + + // Make a new consensus adapter instance. + let adapter = make_consensus_adapter_for_test(state, HashSet::new(), false); + + let checkpoint_summary = CheckpointSummary::new( + &ProtocolConfig::get_for_max_version_UNSAFE(), + 1, + 2, + 10, + &CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]), + None, + GasCostSummary::default(), + None, + 100, + Vec::new(), + ); + + let authority_key = &keys[0]; + let authority = authority_key.public().into(); + let signed_checkpoint_summary = SignedCheckpointSummary::new( + committee.epoch, + checkpoint_summary, + authority_key, + authority, + ); + + let transactions = vec![ConsensusTransaction::new_checkpoint_signature_message( + CheckpointSignatureMessage { + summary: signed_checkpoint_summary, + }, + )]; let waiter = adapter .submit_batch( &transactions, diff --git a/crates/sui-data-ingestion-core/Cargo.toml b/crates/sui-data-ingestion-core/Cargo.toml index 2c14170d76f4a..eb5b040b01d79 100644 --- a/crates/sui-data-ingestion-core/Cargo.toml +++ b/crates/sui-data-ingestion-core/Cargo.toml @@ -20,6 +20,7 @@ object_store.workspace = true prometheus.workspace = true telemetry-subscribers.workspace = true tokio = { workspace = true, features = ["full"] } +tokio-stream.workspace = true tracing.workspace = true sui-storage.workspace = true sui-types.workspace = true diff --git a/crates/sui-data-ingestion-core/src/executor.rs b/crates/sui-data-ingestion-core/src/executor.rs index 1e13d75832f68..5eef85c0920f2 100644 --- a/crates/sui-data-ingestion-core/src/executor.rs +++ b/crates/sui-data-ingestion-core/src/executor.rs @@ -108,6 +108,14 @@ impl IndexerExecutor

{ } Ok(self.progress_store.stats()) } + + pub async fn update_watermark( + &mut self, + task_name: String, + watermark: CheckpointSequenceNumber, + ) -> Result<()> { + self.progress_store.save(task_name, watermark).await + } } pub async fn setup_single_workflow( diff --git a/crates/sui-data-ingestion-core/src/lib.rs b/crates/sui-data-ingestion-core/src/lib.rs index d9b9842921ae1..bfe3adbf49098 100644 --- a/crates/sui-data-ingestion-core/src/lib.rs +++ b/crates/sui-data-ingestion-core/src/lib.rs @@ -5,6 +5,7 @@ mod executor; mod metrics; mod progress_store; mod reader; +mod reducer; #[cfg(test)] mod tests; mod util; @@ -17,25 +18,24 @@ pub use metrics::DataIngestionMetrics; pub use progress_store::{FileProgressStore, ProgressStore, ShimProgressStore}; pub use reader::ReaderOptions; use sui_types::full_checkpoint_content::CheckpointData; -use sui_types::messages_checkpoint::CheckpointSequenceNumber; pub use util::create_remote_store_client; pub use worker_pool::WorkerPool; #[async_trait] pub trait Worker: Send + Sync { - async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result<()>; - /// Optional method. Allows controlling when workflow progress is updated in the progress store. - /// For instance, some pipelines may benefit from aggregating checkpoints, thus skipping - /// the saving of updates for intermediate checkpoints. - /// The default implementation is to update the progress store for every processed checkpoint. - async fn save_progress( - &self, - sequence_number: CheckpointSequenceNumber, - ) -> Option { - Some(sequence_number) - } + type Result: Send + Sync; + async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result; fn preprocess_hook(&self, _: &CheckpointData) -> Result<()> { Ok(()) } } + +#[async_trait] +pub trait Reducer: Send + Sync { + async fn commit(&self, batch: Vec) -> Result<()>; + + fn should_close_batch(&self, _batch: &[R], next_item: Option<&R>) -> bool { + next_item.is_none() + } +} diff --git a/crates/sui-data-ingestion-core/src/reducer.rs b/crates/sui-data-ingestion-core/src/reducer.rs new file mode 100644 index 0000000000000..b8e3105c00673 --- /dev/null +++ b/crates/sui-data-ingestion-core/src/reducer.rs @@ -0,0 +1,59 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{Reducer, Worker, MAX_CHECKPOINTS_IN_PROGRESS}; +use anyhow::Result; +use futures::StreamExt; +use std::collections::HashMap; +use sui_types::messages_checkpoint::CheckpointSequenceNumber; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; + +pub(crate) async fn reduce( + task_name: String, + mut current_checkpoint_number: CheckpointSequenceNumber, + progress_receiver: mpsc::Receiver<(CheckpointSequenceNumber, W::Result)>, + executor_progress_sender: mpsc::Sender<(String, CheckpointSequenceNumber)>, + reducer: Option>>, +) -> Result<()> { + // convert to a stream of MAX size. This way, each iteration of the loop will process all ready messages + let mut stream = + ReceiverStream::new(progress_receiver).ready_chunks(MAX_CHECKPOINTS_IN_PROGRESS); + let mut unprocessed = HashMap::new(); + let mut batch = vec![]; + let mut progress_update = None; + + while let Some(update_batch) = stream.next().await { + for (checkpoint_number, message) in update_batch { + unprocessed.insert(checkpoint_number, message); + } + while let Some(message) = unprocessed.remove(¤t_checkpoint_number) { + if let Some(ref reducer) = reducer { + if reducer.should_close_batch(&batch, Some(&message)) { + reducer.commit(std::mem::take(&mut batch)).await?; + batch = vec![message]; + progress_update = Some(current_checkpoint_number); + } else { + batch.push(message); + } + } + current_checkpoint_number += 1; + } + match reducer { + Some(ref reducer) => { + if reducer.should_close_batch(&batch, None) { + reducer.commit(std::mem::take(&mut batch)).await?; + progress_update = Some(current_checkpoint_number); + } + } + None => progress_update = Some(current_checkpoint_number), + } + if let Some(watermark) = progress_update { + executor_progress_sender + .send((task_name.clone(), watermark)) + .await?; + progress_update = None; + } + } + Ok(()) +} diff --git a/crates/sui-data-ingestion-core/src/tests.rs b/crates/sui-data-ingestion-core/src/tests.rs index 11468de0d6037..4465b0c31c381 100644 --- a/crates/sui-data-ingestion-core/src/tests.rs +++ b/crates/sui-data-ingestion-core/src/tests.rs @@ -74,6 +74,7 @@ struct TestWorker; #[async_trait] impl Worker for TestWorker { + type Result = (); async fn process_checkpoint(&self, _checkpoint: &CheckpointData) -> Result<()> { Ok(()) } diff --git a/crates/sui-data-ingestion-core/src/worker_pool.rs b/crates/sui-data-ingestion-core/src/worker_pool.rs index c4434fa2b0c4f..ff4b38f47122f 100644 --- a/crates/sui-data-ingestion-core/src/worker_pool.rs +++ b/crates/sui-data-ingestion-core/src/worker_pool.rs @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::executor::MAX_CHECKPOINTS_IN_PROGRESS; -use crate::Worker; +use crate::reducer::reduce; +use crate::{Reducer, Worker}; use mysten_metrics::spawn_monitored_task; -use std::collections::{BTreeSet, HashMap, VecDeque}; +use std::collections::{BTreeSet, VecDeque}; use std::sync::Arc; use std::time::Instant; use sui_types::full_checkpoint_content::CheckpointData; @@ -17,6 +18,7 @@ pub struct WorkerPool { pub task_name: String, concurrency: usize, worker: Arc, + reducer: Option>>, } impl WorkerPool { @@ -25,21 +27,35 @@ impl WorkerPool { task_name, concurrency, worker: Arc::new(worker), + reducer: None, } } + pub fn new_with_reducer( + worker: W, + task_name: String, + concurrency: usize, + reducer: Box>, + ) -> Self { + Self { + task_name, + concurrency, + worker: Arc::new(worker), + reducer: Some(reducer), + } + } + pub async fn run( - self, - mut current_checkpoint_number: CheckpointSequenceNumber, + mut self, + watermark: CheckpointSequenceNumber, mut checkpoint_receiver: mpsc::Receiver>, executor_progress_sender: mpsc::Sender<(String, CheckpointSequenceNumber)>, ) { info!( "Starting indexing pipeline {} with concurrency {}. Current watermark is {}.", - self.task_name, self.concurrency, current_checkpoint_number + self.task_name, self.concurrency, watermark ); - let mut updates = HashMap::new(); - let (progress_sender, mut progress_receiver) = mpsc::channel(MAX_CHECKPOINTS_IN_PROGRESS); + let (reducer_sender, reducer_receiver) = mpsc::channel(MAX_CHECKPOINTS_IN_PROGRESS); let mut workers = vec![]; let mut idle: BTreeSet<_> = (0..self.concurrency).collect(); let mut checkpoints = VecDeque::new(); @@ -65,7 +81,7 @@ impl WorkerPool { info!("received checkpoint for processing {} for workflow {}", sequence_number, task_name); let start_time = Instant::now(); let backoff = backoff::ExponentialBackoff::default(); - backoff::future::retry(backoff, || async { + let result = backoff::future::retry(backoff, || async { worker .clone() .process_checkpoint(&checkpoint) @@ -78,7 +94,7 @@ impl WorkerPool { .await .expect("checkpoint processing failed for checkpoint"); info!("finished checkpoint processing {} for workflow {} in {:?}", sequence_number, task_name, start_time.elapsed()); - if cloned_progress_sender.send((worker_id, sequence_number, worker.save_progress(sequence_number).await)).await.is_err() { + if cloned_progress_sender.send((worker_id, sequence_number, result)).await.is_err() { // The progress channel closing is a sign we need to exit this loop. break; } @@ -90,29 +106,20 @@ impl WorkerPool { // Keep all join handles to ensure all workers are terminated before exiting join_handles.push(join_handle); } + spawn_monitored_task!(reduce::( + self.task_name.clone(), + watermark, + reducer_receiver, + executor_progress_sender, + std::mem::take(&mut self.reducer), + )); // main worker pool loop loop { tokio::select! { - Some((worker_id, status_update, progress_watermark)) = progress_receiver.recv() => { + Some((worker_id, checkpoint_number, message)) = progress_receiver.recv() => { idle.insert(worker_id); - updates.insert(status_update, progress_watermark); - if status_update == current_checkpoint_number { - let mut executor_status_update = None; - while let Some(progress_watermark) = updates.remove(¤t_checkpoint_number) { - if let Some(watermark) = progress_watermark { - executor_status_update = Some(watermark + 1); - } - current_checkpoint_number += 1; - } - if let Some(update) = executor_status_update { - if executor_progress_sender - .send((self.task_name.clone(), update)) - .await.is_err() { - // The executor progress channel closing is a sign we need to - // exit this loop. - break; - } - } + if reducer_sender.send((checkpoint_number, message)).await.is_err() { + break; } while !checkpoints.is_empty() && !idle.is_empty() { let checkpoint = checkpoints.pop_front().unwrap(); @@ -129,7 +136,7 @@ impl WorkerPool { } let checkpoint = maybe_checkpoint.expect("invariant's checked"); let sequence_number = checkpoint.checkpoint_summary.sequence_number; - if sequence_number < current_checkpoint_number { + if sequence_number < watermark { continue; } self.worker.preprocess_hook(&checkpoint).expect("failed to preprocess task"); diff --git a/crates/sui-data-ingestion/Cargo.toml b/crates/sui-data-ingestion/Cargo.toml index 174202f303878..2450691f4cdd6 100644 --- a/crates/sui-data-ingestion/Cargo.toml +++ b/crates/sui-data-ingestion/Cargo.toml @@ -32,6 +32,7 @@ sui-archival.workspace = true sui-storage.workspace = true sui-data-ingestion-core.workspace = true sui-types.workspace = true +tempfile.workspace = true url.workspace = true [dev-dependencies] diff --git a/crates/sui-data-ingestion/src/bin/archival_ingestion.rs b/crates/sui-data-ingestion/src/bin/archival_ingestion.rs index 28ea1e77ff878..1acabe874dc0b 100644 --- a/crates/sui-data-ingestion/src/bin/archival_ingestion.rs +++ b/crates/sui-data-ingestion/src/bin/archival_ingestion.rs @@ -2,9 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::Result; +use prometheus::Registry; use serde::{Deserialize, Serialize}; -use sui_data_ingestion::{ArchivalConfig, ArchivalWorker}; -use sui_data_ingestion_core::setup_single_workflow; +use sui_data_ingestion::{ArchivalConfig, ArchivalReducer, ArchivalWorker}; +use sui_data_ingestion_core::{ + DataIngestionMetrics, IndexerExecutor, ReaderOptions, ShimProgressStore, WorkerPool, +}; +use tokio::sync::oneshot; #[derive(Debug, Clone, Serialize, Deserialize)] struct Config { @@ -37,17 +41,25 @@ async fn main() -> Result<()> { commit_file_size: config.commit_file_size, commit_duration_seconds: config.commit_duration_seconds, }; - let worker = ArchivalWorker::new(archival_config).await?; - let initial_checkpoint_number = worker.initial_checkpoint_number().await; - - let (executor, _exit_sender) = setup_single_workflow( - worker, - config.remote_store_url, - initial_checkpoint_number, + let (_exit_sender, exit_receiver) = oneshot::channel(); + let reducer = ArchivalReducer::new(archival_config).await?; + let progress_store = ShimProgressStore(reducer.get_watermark().await?); + let mut executor = IndexerExecutor::new( + progress_store, 1, - None, - ) - .await?; - executor.await?; + DataIngestionMetrics::new(&Registry::new()), + ); + let worker_pool = + WorkerPool::new_with_reducer(ArchivalWorker, "archival".to_string(), 1, Box::new(reducer)); + executor.register(worker_pool).await?; + executor + .run( + tempfile::tempdir()?.into_path(), + Some(config.remote_store_url), + vec![], + ReaderOptions::default(), + exit_receiver, + ) + .await?; Ok(()) } diff --git a/crates/sui-data-ingestion/src/lib.rs b/crates/sui-data-ingestion/src/lib.rs index f5a53324ac8e8..3ef48d9ec9713 100644 --- a/crates/sui-data-ingestion/src/lib.rs +++ b/crates/sui-data-ingestion/src/lib.rs @@ -6,5 +6,6 @@ mod workers; pub use progress_store::DynamoDBProgressStore; pub use workers::{ - ArchivalConfig, ArchivalWorker, BlobTaskConfig, BlobWorker, KVStoreTaskConfig, KVStoreWorker, + ArchivalConfig, ArchivalReducer, ArchivalWorker, BlobTaskConfig, BlobWorker, KVStoreTaskConfig, + KVStoreWorker, }; diff --git a/crates/sui-data-ingestion/src/main.rs b/crates/sui-data-ingestion/src/main.rs index a55ad5535c5ec..0a03b9af29591 100644 --- a/crates/sui-data-ingestion/src/main.rs +++ b/crates/sui-data-ingestion/src/main.rs @@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize}; use std::env; use std::path::PathBuf; use sui_data_ingestion::{ - ArchivalConfig, ArchivalWorker, BlobTaskConfig, BlobWorker, DynamoDBProgressStore, - KVStoreTaskConfig, KVStoreWorker, + ArchivalConfig, ArchivalReducer, ArchivalWorker, BlobTaskConfig, BlobWorker, + DynamoDBProgressStore, KVStoreTaskConfig, KVStoreWorker, }; use sui_data_ingestion_core::{DataIngestionMetrics, ReaderOptions}; use sui_data_ingestion_core::{IndexerExecutor, WorkerPool}; @@ -118,10 +118,15 @@ async fn main() -> Result<()> { for task_config in config.tasks { match task_config.task { Task::Archival(archival_config) => { - let worker_pool = WorkerPool::new( - ArchivalWorker::new(archival_config).await?, + let reducer = ArchivalReducer::new(archival_config).await?; + executor + .update_watermark(task_config.name.clone(), reducer.get_watermark().await?) + .await?; + let worker_pool = WorkerPool::new_with_reducer( + ArchivalWorker, task_config.name, task_config.concurrency, + Box::new(reducer), ); executor.register(worker_pool).await?; } diff --git a/crates/sui-data-ingestion/src/workers/archival.rs b/crates/sui-data-ingestion/src/workers/archival.rs index 1d5bf2700cf2e..3d6c9587ebb71 100644 --- a/crates/sui-data-ingestion/src/workers/archival.rs +++ b/crates/sui-data-ingestion/src/workers/archival.rs @@ -10,18 +10,16 @@ use object_store::path::Path; use object_store::ObjectStore; use serde::{Deserialize, Serialize}; use std::io::Cursor; -use std::ops::Range; use sui_archival::{ create_file_metadata_from_bytes, finalize_manifest, read_manifest_from_bytes, FileType, Manifest, CHECKPOINT_FILE_MAGIC, SUMMARY_FILE_MAGIC, }; -use sui_data_ingestion_core::{create_remote_store_client, Worker}; +use sui_data_ingestion_core::{create_remote_store_client, Reducer, Worker}; use sui_storage::blob::{Blob, BlobEncoding}; use sui_storage::{compress, FileCompression, StorageFormat}; use sui_types::base_types::{EpochId, ExecutionData}; use sui_types::full_checkpoint_content::CheckpointData; use sui_types::messages_checkpoint::{CheckpointSequenceNumber, FullCheckpointContents}; -use tokio::sync::Mutex; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ArchivalConfig { @@ -31,90 +29,67 @@ pub struct ArchivalConfig { pub commit_duration_seconds: u64, } -struct AccumulatedState { - epoch: EpochId, - checkpoint_range: Range, - buffer: Vec, - summary_buffer: Vec, - last_commit_ms: u64, - should_update_progress: bool, +pub struct ArchivalWorker; +#[async_trait] +impl Worker for ArchivalWorker { + type Result = CheckpointData; + async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result { + Ok(checkpoint.clone()) + } } -pub struct ArchivalWorker { +pub struct ArchivalReducer { remote_store: Box, - state: Mutex, - commit_file_size: usize, commit_duration_ms: u64, } -impl ArchivalWorker { +impl ArchivalReducer { pub async fn new(config: ArchivalConfig) -> Result { let remote_store = create_remote_store_client(config.remote_url, config.remote_store_options, 10)?; - let manifest = Self::read_manifest(&remote_store).await?; - let state = AccumulatedState { - epoch: manifest.epoch_num(), - checkpoint_range: manifest.next_checkpoint_seq_num() - ..manifest.next_checkpoint_seq_num(), - buffer: vec![], - summary_buffer: vec![], - last_commit_ms: 0, - should_update_progress: false, - }; Ok(Self { remote_store, - state: Mutex::new(state), - commit_file_size: config.commit_file_size, commit_duration_ms: config.commit_duration_seconds * 1000, }) } - - async fn read_manifest(remote_store: &dyn ObjectStore) -> Result { - Ok(match remote_store.get(&Path::from("MANIFEST")).await { - Ok(resp) => read_manifest_from_bytes(resp.bytes().await?.to_vec())?, - Err(err) if err.to_string().contains("404") => Manifest::new(0, 0), - Err(err) => Err(err)?, - }) - } - - async fn upload(&self, state: &AccumulatedState) -> Result<()> { - let checkpoint_file_path = - format!("epoch_{}/{}.chk", state.epoch, state.checkpoint_range.start); + async fn upload( + &self, + epoch: EpochId, + start: CheckpointSequenceNumber, + end: CheckpointSequenceNumber, + summary_buffer: Vec, + buffer: Vec, + ) -> Result<()> { + let checkpoint_file_path = format!("epoch_{}/{}.chk", epoch, start); let chk_bytes = self .upload_file( Path::from(checkpoint_file_path.clone()), CHECKPOINT_FILE_MAGIC, - &state.buffer, + &buffer, ) .await?; - let summary_file_path = - format!("epoch_{}/{}.sum", state.epoch, state.checkpoint_range.start); + let summary_file_path = format!("epoch_{}/{}.sum", epoch, start); let sum_bytes = self .upload_file( Path::from(summary_file_path.clone()), SUMMARY_FILE_MAGIC, - &state.summary_buffer, + &summary_buffer, ) .await?; let mut manifest = Self::read_manifest(&self.remote_store).await?; let checkpoint_file_metadata = create_file_metadata_from_bytes( chk_bytes, FileType::CheckpointContent, - state.epoch, - state.checkpoint_range.clone(), + epoch, + start..end, )?; let summary_file_metadata = create_file_metadata_from_bytes( sum_bytes, FileType::CheckpointSummary, - state.epoch, - state.checkpoint_range.clone(), + epoch, + start..end, )?; - manifest.update( - state.epoch, - state.checkpoint_range.end, - checkpoint_file_metadata, - summary_file_metadata, - ); + manifest.update(epoch, end, checkpoint_file_metadata, summary_file_metadata); let bytes = finalize_manifest(manifest)?; self.remote_store @@ -122,7 +97,6 @@ impl ArchivalWorker { .await?; Ok(()) } - async fn upload_file(&self, location: Path, magic: u32, content: &[u8]) -> Result { let mut buffer = vec![0; 4]; BigEndian::write_u32(&mut buffer, magic); @@ -138,67 +112,69 @@ impl ArchivalWorker { Ok(Bytes::from(compressed_buffer)) } - pub async fn initial_checkpoint_number(&self) -> CheckpointSequenceNumber { - self.state.lock().await.checkpoint_range.start + pub async fn get_watermark(&self) -> Result { + let manifest = Self::read_manifest(&self.remote_store).await?; + Ok(manifest.next_checkpoint_seq_num()) + } + async fn read_manifest(remote_store: &dyn ObjectStore) -> Result { + Ok(match remote_store.get(&Path::from("MANIFEST")).await { + Ok(resp) => read_manifest_from_bytes(resp.bytes().await?.to_vec())?, + Err(err) if err.to_string().contains("404") => Manifest::new(0, 0), + Err(err) => Err(err)?, + }) } } #[async_trait] -impl Worker for ArchivalWorker { - async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result<()> { - let mut state = self.state.lock().await; - let sequence_number = checkpoint.checkpoint_summary.sequence_number; - if sequence_number < state.checkpoint_range.start { - return Ok(()); - } - let epoch = checkpoint.checkpoint_summary.epoch; - if state.buffer.is_empty() { - assert!(epoch == state.epoch || epoch == state.epoch + 1); - state.epoch = epoch; - state.last_commit_ms = checkpoint.checkpoint_summary.timestamp_ms; +impl Reducer for ArchivalReducer { + async fn commit(&self, batch: Vec) -> Result<()> { + if batch.is_empty() { + return Err(anyhow::anyhow!("commit batch can't be empty")); } - let full_checkpoint_contents = FullCheckpointContents::from_contents_and_execution_data( - checkpoint.checkpoint_contents.clone(), - checkpoint - .transactions - .iter() - .map(|t| ExecutionData::new(t.transaction.clone(), t.effects.clone())), - ); - let contents_blob = Blob::encode(&full_checkpoint_contents, BlobEncoding::Bcs)?; - let blob_size = contents_blob.size(); - let summary_blob = Blob::encode(&checkpoint.checkpoint_summary, BlobEncoding::Bcs)?; - - if !state.buffer.is_empty() - && (((state.buffer.len() + blob_size) > self.commit_file_size) - || state.epoch != epoch - || checkpoint.checkpoint_summary.timestamp_ms - > (self.commit_duration_ms + state.last_commit_ms)) - { - self.upload(&state).await?; - state.epoch = epoch; - state.checkpoint_range = sequence_number..sequence_number; - state.buffer = vec![]; - state.summary_buffer = vec![]; - state.last_commit_ms = checkpoint.checkpoint_summary.timestamp_ms; - state.should_update_progress = true; + let mut summary_buffer = vec![]; + let mut buffer = vec![]; + let first_checkpoint = &batch[0]; + let epoch = first_checkpoint.checkpoint_summary.epoch; + let start_checkpoint = first_checkpoint.checkpoint_summary.sequence_number; + let mut last_checkpoint = start_checkpoint; + for checkpoint in batch { + let full_checkpoint_contents = FullCheckpointContents::from_contents_and_execution_data( + checkpoint.checkpoint_contents.clone(), + checkpoint + .transactions + .iter() + .map(|t| ExecutionData::new(t.transaction.clone(), t.effects.clone())), + ); + let contents_blob = Blob::encode(&full_checkpoint_contents, BlobEncoding::Bcs)?; + let summary_blob = Blob::encode(&checkpoint.checkpoint_summary, BlobEncoding::Bcs)?; + contents_blob.write(&mut buffer)?; + summary_blob.write(&mut summary_buffer)?; + last_checkpoint += 1; } - contents_blob.write(&mut state.buffer)?; - summary_blob.write(&mut state.summary_buffer)?; - state.checkpoint_range.end += 1; + self.upload( + epoch, + start_checkpoint, + last_checkpoint, + summary_buffer, + buffer, + ) + .await?; Ok(()) } - async fn save_progress( + fn should_close_batch( &self, - sequence_number: CheckpointSequenceNumber, - ) -> Option { - let mut state = self.state.lock().await; - let should_update_progress = state.should_update_progress; - state.should_update_progress = false; - if should_update_progress && sequence_number > 0 { - Some(sequence_number - 1) - } else { - None + batch: &[CheckpointData], + next_item: Option<&CheckpointData>, + ) -> bool { + // never close a batch without a trigger condition + if batch.is_empty() || next_item.is_none() { + return false; } + let first_checkpoint = &batch[0].checkpoint_summary; + let next_checkpoint = next_item.expect("invariant's checked"); + next_checkpoint.checkpoint_summary.epoch != first_checkpoint.epoch + || next_checkpoint.checkpoint_summary.timestamp_ms + > (self.commit_duration_ms + first_checkpoint.timestamp_ms) } } diff --git a/crates/sui-data-ingestion/src/workers/blob.rs b/crates/sui-data-ingestion/src/workers/blob.rs index 370f6010a71b1..1344d9748d72c 100644 --- a/crates/sui-data-ingestion/src/workers/blob.rs +++ b/crates/sui-data-ingestion/src/workers/blob.rs @@ -32,6 +32,7 @@ impl BlobWorker { #[async_trait] impl Worker for BlobWorker { + type Result = (); async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result<()> { let bytes = Blob::encode(checkpoint, BlobEncoding::Bcs)?.to_bytes(); let location = Path::from(format!( diff --git a/crates/sui-data-ingestion/src/workers/kv_store.rs b/crates/sui-data-ingestion/src/workers/kv_store.rs index fce7718f43959..bddefd41da686 100644 --- a/crates/sui-data-ingestion/src/workers/kv_store.rs +++ b/crates/sui-data-ingestion/src/workers/kv_store.rs @@ -176,6 +176,8 @@ impl KVStoreWorker { #[async_trait] impl Worker for KVStoreWorker { + type Result = (); + async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result<()> { let mut transactions = vec![]; let mut effects = vec![]; diff --git a/crates/sui-data-ingestion/src/workers/mod.rs b/crates/sui-data-ingestion/src/workers/mod.rs index a2ad63a52752d..733123bded77d 100644 --- a/crates/sui-data-ingestion/src/workers/mod.rs +++ b/crates/sui-data-ingestion/src/workers/mod.rs @@ -4,6 +4,6 @@ mod archival; mod blob; mod kv_store; -pub use archival::{ArchivalConfig, ArchivalWorker}; +pub use archival::{ArchivalConfig, ArchivalReducer, ArchivalWorker}; pub use blob::{BlobTaskConfig, BlobWorker}; pub use kv_store::{KVStoreTaskConfig, KVStoreWorker}; diff --git a/crates/sui-deepbook-indexer/Cargo.toml b/crates/sui-deepbook-indexer/Cargo.toml index b32a1769c6c9d..a416198b6057d 100644 --- a/crates/sui-deepbook-indexer/Cargo.toml +++ b/crates/sui-deepbook-indexer/Cargo.toml @@ -21,7 +21,6 @@ clap.workspace = true mysten-metrics.workspace = true prometheus.workspace = true serde_yaml.workspace = true -sui-bridge.workspace = true sui-sdk.workspace = true sui-json-rpc-types.workspace = true sui-data-ingestion-core.workspace = true @@ -32,12 +31,11 @@ backoff.workspace = true sui-config.workspace = true sui-indexer-builder.workspace = true tempfile.workspace = true -bigdecimal = "0.4.0" +axum.workspace = true +bigdecimal = { version = "0.4.5" } [dev-dependencies] sui-types = { workspace = true, features = ["test-utils"] } -sui-test-transaction-builder.workspace = true -test-cluster.workspace = true hex-literal = "0.3.4" [[bin]] diff --git a/crates/sui-deepbook-indexer/config.yaml b/crates/sui-deepbook-indexer/config.yaml index e5a3b12686932..5d6dd23638d41 100644 --- a/crates/sui-deepbook-indexer/config.yaml +++ b/crates/sui-deepbook-indexer/config.yaml @@ -16,7 +16,3 @@ # metric_url: # Client metric port # metric_port: -# checkpoint size of each backfill worker, use 432000 for 1 worker per day, assume 5 checkpoint per second -# back_fill_lot_size: -# Optional starting checkpoint for realtime ingestion task -# resume_from_checkpoint: \ No newline at end of file diff --git a/crates/sui-deepbook-indexer/src/config.rs b/crates/sui-deepbook-indexer/src/config.rs index 363d722451061..3e854d87f3d42 100644 --- a/crates/sui-deepbook-indexer/src/config.rs +++ b/crates/sui-deepbook-indexer/src/config.rs @@ -17,7 +17,7 @@ pub struct IndexerConfig { pub deepbook_genesis_checkpoint: u64, pub concurrency: u64, pub metric_port: u16, - pub resume_from_checkpoint: Option, + pub service_port: u16, } impl sui_config::Config for IndexerConfig {} diff --git a/crates/sui-deepbook-indexer/src/error.rs b/crates/sui-deepbook-indexer/src/error.rs new file mode 100644 index 0000000000000..525cb8d0b4d11 --- /dev/null +++ b/crates/sui-deepbook-indexer/src/error.rs @@ -0,0 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[derive(Debug, Clone)] +pub enum DeepBookError { + InternalError(String), +} diff --git a/crates/sui-deepbook-indexer/src/lib.rs b/crates/sui-deepbook-indexer/src/lib.rs index 3c461811dc991..80a90aebfbeeb 100644 --- a/crates/sui-deepbook-indexer/src/lib.rs +++ b/crates/sui-deepbook-indexer/src/lib.rs @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 pub mod config; +pub mod error; pub mod events; pub mod metrics; pub mod models; pub mod postgres_manager; pub mod schema; +pub mod server; pub mod types; pub mod sui_datasource; diff --git a/crates/sui-deepbook-indexer/src/main.rs b/crates/sui-deepbook-indexer/src/main.rs index 3ef803c77a5b4..4e47ffaba2b03 100644 --- a/crates/sui-deepbook-indexer/src/main.rs +++ b/crates/sui-deepbook-indexer/src/main.rs @@ -14,6 +14,7 @@ use sui_data_ingestion_core::DataIngestionMetrics; use sui_deepbook_indexer::config::IndexerConfig; use sui_deepbook_indexer::metrics::DeepBookIndexerMetrics; use sui_deepbook_indexer::postgres_manager::get_connection_pool; +use sui_deepbook_indexer::server::run_server; use sui_deepbook_indexer::sui_datasource::SuiCheckpointDatasource; use sui_deepbook_indexer::sui_deepbook_indexer::PgDeepbookPersistent; use sui_deepbook_indexer::sui_deepbook_indexer::SuiDeepBookDataMapper; @@ -84,6 +85,11 @@ async fn main() -> Result<()> { ingestion_metrics.clone(), indexer_meterics.clone(), ); + + let service_address = + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), config.service_port); + run_server(service_address, datastore.clone()); + let indexer = IndexerBuilder::new( "SuiDeepBookIndexer", sui_checkpoint_datasource, diff --git a/crates/sui-deepbook-indexer/src/models.rs b/crates/sui-deepbook-indexer/src/models.rs index f6cb9e26dc1bf..d0d57671e3bce 100644 --- a/crates/sui-deepbook-indexer/src/models.rs +++ b/crates/sui-deepbook-indexer/src/models.rs @@ -4,11 +4,12 @@ use diesel::data_types::PgTimestamp; use diesel::{Identifiable, Insertable, Queryable, Selectable}; +use serde::Serialize; use sui_indexer_builder::{Task, LIVE_TASK_TARGET_CHECKPOINT}; use crate::schema::{ - balances, flashloans, order_fills, order_updates, pool_prices, progress_store, proposals, - rebates, stakes, sui_error_transactions, trade_params_update, votes, + balances, flashloans, order_fills, order_updates, pool_prices, pools, progress_store, + proposals, rebates, stakes, sui_error_transactions, trade_params_update, votes, }; #[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] @@ -57,6 +58,13 @@ pub struct OrderFill { pub onchain_timestamp: i64, } +#[derive(Queryable)] +pub struct OrderFillSummary { + pub maker_balance_manager_id: String, + pub taker_balance_manager_id: String, + pub base_quantity: i64, +} + #[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] #[diesel(table_name = flashloans, primary_key(digest))] pub struct Flashloan { @@ -164,6 +172,24 @@ pub struct Votes { pub stake: i64, } +#[derive(Queryable, Selectable, Insertable, Identifiable, Debug, Serialize)] +#[diesel(table_name = pools, primary_key(pool_id))] +pub struct Pools { + pub pool_id: String, + pub pool_name: String, + pub base_asset_id: String, + pub base_asset_decimals: i16, + pub base_asset_symbol: String, + pub base_asset_name: String, + pub quote_asset_id: String, + pub quote_asset_decimals: i16, + pub quote_asset_symbol: String, + pub quote_asset_name: String, + pub min_size: i32, + pub lot_size: i32, + pub tick_size: i32, +} + #[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] #[diesel(table_name = sui_error_transactions, primary_key(txn_digest))] pub struct SuiErrorTransactions { diff --git a/crates/sui-deepbook-indexer/src/server.rs b/crates/sui-deepbook-indexer/src/server.rs new file mode 100644 index 0000000000000..1f2a5a9985be6 --- /dev/null +++ b/crates/sui-deepbook-indexer/src/server.rs @@ -0,0 +1,144 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + error::DeepBookError, + models::{OrderFillSummary, Pools}, + schema, + sui_deepbook_indexer::PgDeepbookPersistent, +}; +use axum::{ + debug_handler, + extract::{Path, State}, + http::StatusCode, + routing::get, + Json, Router, +}; +use diesel::BoolExpressionMethods; +use diesel::QueryDsl; +use diesel::{ExpressionMethods, SelectableHelper}; +use diesel_async::RunQueryDsl; +use std::net::SocketAddr; +use std::time::{SystemTime, UNIX_EPOCH}; +use tokio::{net::TcpListener, task::JoinHandle}; + +pub const GET_POOLS_PATH: &str = "/get_pools"; +pub const GET_24HR_VOLUME_PATH: &str = "/get_24hr_volume/:pool_id"; +pub const GET_24HR_VOLUME_BY_BALANCE_MANAGER_ID: &str = + "/get_24hr_volume_by_balance_manager_id/:pool_id/:balance_manager_id"; + +pub fn run_server(socket_address: SocketAddr, state: PgDeepbookPersistent) -> JoinHandle<()> { + tokio::spawn(async move { + let listener = TcpListener::bind(socket_address).await.unwrap(); + axum::serve(listener, make_router(state)).await.unwrap(); + }) +} + +pub(crate) fn make_router(state: PgDeepbookPersistent) -> Router { + Router::new() + .route("/", get(health_check)) + .route(GET_POOLS_PATH, get(get_pools)) + .route(GET_24HR_VOLUME_PATH, get(get_24hr_volume)) + .route( + GET_24HR_VOLUME_BY_BALANCE_MANAGER_ID, + get(get_24hr_volume_by_balance_manager_id), + ) + .with_state(state) +} + +impl axum::response::IntoResponse for DeepBookError { + // TODO: distinguish client error. + fn into_response(self) -> axum::response::Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {:?}", self), + ) + .into_response() + } +} + +impl From for DeepBookError +where + E: Into, +{ + fn from(err: E) -> Self { + Self::InternalError(err.into().to_string()) + } +} + +async fn health_check() -> StatusCode { + StatusCode::OK +} + +/// Get all pools stored in database +#[debug_handler] +async fn get_pools( + State(state): State, +) -> Result>, DeepBookError> { + let connection = &mut state.pool.get().await?; + let results = schema::pools::table + .select(Pools::as_select()) + .load(connection) + .await?; + + Ok(Json(results)) +} + +async fn get_24hr_volume( + Path(pool_id): Path, + State(state): State, +) -> Result, DeepBookError> { + let connection = &mut state.pool.get().await?; + let unix_ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64; + let day_ago = unix_ts - 24 * 60 * 60 * 1000; + let vols: Vec = schema::order_fills::table + .select(schema::order_fills::base_quantity) + .filter(schema::order_fills::pool_id.eq(pool_id)) + .filter(schema::order_fills::onchain_timestamp.gt(day_ago)) + .load(connection) + .await?; + Ok(Json(vols.into_iter().map(|v| v as u64).sum())) +} + +async fn get_24hr_volume_by_balance_manager_id( + Path((pool_id, balance_manager_id)): Path<(String, String)>, + State(state): State, +) -> Result>, DeepBookError> { + let connection = &mut state.pool.get().await?; + let unix_ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64; + let day_ago = unix_ts - 24 * 60 * 60 * 1000; + let results: Vec = schema::order_fills::table + .select(( + schema::order_fills::maker_balance_manager_id, + schema::order_fills::taker_balance_manager_id, + schema::order_fills::base_quantity, + )) + .filter(schema::order_fills::pool_id.eq(pool_id)) + .filter(schema::order_fills::onchain_timestamp.gt(day_ago)) + .filter( + schema::order_fills::maker_balance_manager_id + .eq(&balance_manager_id) + .or(schema::order_fills::taker_balance_manager_id.eq(&balance_manager_id)), + ) + .load(connection) + .await?; + + let mut maker_vol = 0; + let mut taker_vol = 0; + for order_fill in results { + if order_fill.maker_balance_manager_id == balance_manager_id { + maker_vol += order_fill.base_quantity; + }; + if order_fill.taker_balance_manager_id == balance_manager_id { + taker_vol += order_fill.base_quantity; + }; + } + + Ok(Json(vec![maker_vol, taker_vol])) +} diff --git a/crates/sui-deepbook-indexer/src/sui_datasource.rs b/crates/sui-deepbook-indexer/src/sui_datasource.rs index 3da75a37eaf0e..3ca231bfc15a9 100644 --- a/crates/sui-deepbook-indexer/src/sui_datasource.rs +++ b/crates/sui-deepbook-indexer/src/sui_datasource.rs @@ -161,6 +161,8 @@ pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); #[async_trait] impl Worker for IndexerWorker { + type Result = (); + async fn process_checkpoint(&self, checkpoint: &SuiCheckpointData) -> anyhow::Result<()> { tracing::trace!( "Received checkpoint [{}] {}: {}", diff --git a/crates/sui-deepbook-indexer/src/sui_deepbook_indexer.rs b/crates/sui-deepbook-indexer/src/sui_deepbook_indexer.rs index 99f709ece2b07..92eac3b0ad449 100644 --- a/crates/sui-deepbook-indexer/src/sui_deepbook_indexer.rs +++ b/crates/sui-deepbook-indexer/src/sui_deepbook_indexer.rs @@ -43,7 +43,7 @@ use crate::{models, schema}; /// Persistent layer impl #[derive(Clone)] pub struct PgDeepbookPersistent { - pool: PgPool, + pub pool: PgPool, save_progress_policy: ProgressSavingPolicy, } diff --git a/crates/sui-e2e-tests/Cargo.toml b/crates/sui-e2e-tests/Cargo.toml index 970458f3a9e94..9d4370817302d 100644 --- a/crates/sui-e2e-tests/Cargo.toml +++ b/crates/sui-e2e-tests/Cargo.toml @@ -64,6 +64,7 @@ sui-sdk.workspace = true sui-keys.workspace = true sui-rest-api.workspace = true shared-crypto.workspace = true +sui-sdk-types.workspace = true passkey-types.workspace = true passkey-client.workspace = true diff --git a/crates/sui-e2e-tests/tests/rest.rs b/crates/sui-e2e-tests/tests/rest.rs index ff408edee6549..e1e35fe5588b0 100644 --- a/crates/sui-e2e-tests/tests/rest.rs +++ b/crates/sui-e2e-tests/tests/rest.rs @@ -1,10 +1,22 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use shared_crypto::intent::Intent; +use sui_keys::keystore::AccountKeystore; use sui_macros::sim_test; +use sui_rest_api::client::reqwest::StatusCode; use sui_rest_api::client::BalanceChange; +use sui_rest_api::transactions::ResolveTransactionQueryParameters; use sui_rest_api::Client; use sui_rest_api::ExecuteTransactionQueryParameters; +use sui_sdk_types::types::Argument; +use sui_sdk_types::types::Command; +use sui_sdk_types::types::TransactionExpiration; +use sui_sdk_types::types::UnresolvedGasPayment; +use sui_sdk_types::types::UnresolvedInputArgument; +use sui_sdk_types::types::UnresolvedObjectReference; +use sui_sdk_types::types::UnresolvedProgrammableTransaction; +use sui_sdk_types::types::UnresolvedTransaction; use sui_test_transaction_builder::make_transfer_sui_transaction; use sui_types::base_types::SuiAddress; use sui_types::effects::TransactionEffectsAPI; @@ -53,3 +65,340 @@ async fn execute_transaction_transfer() { assert_eq!(actual, expected); } + +#[sim_test] +async fn resolve_transaction_simple_transfer() { + let test_cluster = TestClusterBuilder::new().build().await; + + let client = Client::new(test_cluster.rpc_url()); + let recipient = SuiAddress::random_for_testing_only(); + + let (sender, mut gas) = test_cluster.wallet.get_one_account().await.unwrap(); + gas.sort_by_key(|object_ref| object_ref.0); + let obj_to_send = gas.first().unwrap().0; + + let unresolved_transaction = UnresolvedTransaction { + ptb: UnresolvedProgrammableTransaction { + inputs: vec![ + UnresolvedInputArgument::ImmutableOrOwned(UnresolvedObjectReference { + object_id: obj_to_send.into(), + version: None, + digest: None, + }), + UnresolvedInputArgument::Pure { + value: bcs::to_bytes(&recipient).unwrap(), + }, + ], + commands: vec![Command::TransferObjects( + sui_sdk_types::types::TransferObjects { + objects: vec![Argument::Input(0)], + address: Argument::Input(1), + }, + )], + }, + sender: sender.into(), + gas_payment: None, + expiration: TransactionExpiration::None, + }; + + let resolved = client + .inner() + .resolve_transaction_with_parameters( + &unresolved_transaction, + &ResolveTransactionQueryParameters { + simulate: true, + ..Default::default() + }, + ) + .await + .unwrap() + .into_inner(); + + let signed_transaction = test_cluster + .wallet + .sign_transaction(&resolved.transaction.try_into().unwrap()); + let effects = client + .execute_transaction( + &ExecuteTransactionQueryParameters::default(), + &signed_transaction, + ) + .await + .unwrap() + .effects; + + assert!(effects.status().is_ok()); + assert_eq!( + resolved.simulation.unwrap().effects, + effects.try_into().unwrap() + ); +} + +#[sim_test] +async fn resolve_transaction_transfer_with_sponsor() { + let test_cluster = TestClusterBuilder::new().build().await; + + let client = Client::new(test_cluster.rpc_url()); + let recipient = SuiAddress::random_for_testing_only(); + + let (sender, gas) = test_cluster.wallet.get_one_account().await.unwrap(); + let obj_to_send = gas.first().unwrap().0; + let sponsor = test_cluster.wallet.get_addresses()[1]; + + let unresolved_transaction = UnresolvedTransaction { + ptb: UnresolvedProgrammableTransaction { + inputs: vec![ + UnresolvedInputArgument::ImmutableOrOwned(UnresolvedObjectReference { + object_id: obj_to_send.into(), + version: None, + digest: None, + }), + UnresolvedInputArgument::Pure { + value: bcs::to_bytes(&recipient).unwrap(), + }, + ], + commands: vec![Command::TransferObjects( + sui_sdk_types::types::TransferObjects { + objects: vec![Argument::Input(0)], + address: Argument::Input(1), + }, + )], + }, + sender: sender.into(), + gas_payment: Some(UnresolvedGasPayment { + objects: vec![], + owner: sponsor.into(), + price: None, + budget: None, + }), + expiration: TransactionExpiration::None, + }; + + let resolved = client + .inner() + .resolve_transaction_with_parameters( + &unresolved_transaction, + &ResolveTransactionQueryParameters { + simulate: true, + ..Default::default() + }, + ) + .await + .unwrap() + .into_inner(); + + let transaction_data = resolved.transaction.clone().try_into().unwrap(); + let sender_sig = test_cluster + .wallet + .config + .keystore + .sign_secure(&sender, &transaction_data, Intent::sui_transaction()) + .unwrap(); + let sponsor_sig = test_cluster + .wallet + .config + .keystore + .sign_secure(&sponsor, &transaction_data, Intent::sui_transaction()) + .unwrap(); + + let signed_transaction = sui_types::transaction::Transaction::from_data( + transaction_data, + vec![sender_sig, sponsor_sig], + ); + let effects = client + .execute_transaction( + &ExecuteTransactionQueryParameters::default(), + &signed_transaction, + ) + .await + .unwrap() + .effects; + + assert!(effects.status().is_ok()); + assert_eq!( + resolved.simulation.unwrap().effects, + effects.try_into().unwrap() + ); +} + +#[sim_test] +async fn resolve_transaction_borrowed_shared_object() { + let test_cluster = TestClusterBuilder::new().build().await; + + let client = Client::new(test_cluster.rpc_url()); + + let sender = test_cluster.wallet.get_addresses()[0]; + + let unresolved_transaction = UnresolvedTransaction { + ptb: UnresolvedProgrammableTransaction { + inputs: vec![UnresolvedInputArgument::Shared { + object_id: "0x6".parse().unwrap(), + initial_shared_version: None, + mutable: None, + }], + commands: vec![Command::MoveCall(sui_sdk_types::types::MoveCall { + package: "0x2".parse().unwrap(), + module: "clock".parse().unwrap(), + function: "timestamp_ms".parse().unwrap(), + type_arguments: vec![], + arguments: vec![Argument::Input(0)], + })], + }, + sender: sender.into(), + gas_payment: None, + expiration: TransactionExpiration::None, + }; + + let resolved = client + .inner() + .resolve_transaction_with_parameters( + &unresolved_transaction, + &ResolveTransactionQueryParameters { + simulate: true, + ..Default::default() + }, + ) + .await + .unwrap() + .into_inner(); + + let signed_transaction = test_cluster + .wallet + .sign_transaction(&resolved.transaction.try_into().unwrap()); + let effects = client + .execute_transaction( + &ExecuteTransactionQueryParameters::default(), + &signed_transaction, + ) + .await + .unwrap() + .effects; + + assert!(effects.status().is_ok()); +} + +#[sim_test] +async fn resolve_transaction_mutable_shared_object() { + let test_cluster = TestClusterBuilder::new().build().await; + + let client = Client::new(test_cluster.rpc_url()); + + let (sender, mut gas) = test_cluster.wallet.get_one_account().await.unwrap(); + gas.sort_by_key(|object_ref| object_ref.0); + let obj_to_stake = gas.first().unwrap().0; + let validator_address = client + .inner() + .get_system_state_summary() + .await + .unwrap() + .inner() + .active_validators + .first() + .unwrap() + .address; + + let unresolved_transaction = UnresolvedTransaction { + ptb: UnresolvedProgrammableTransaction { + inputs: vec![ + UnresolvedInputArgument::Shared { + object_id: "0x5".parse().unwrap(), + initial_shared_version: None, + mutable: None, + }, + UnresolvedInputArgument::ImmutableOrOwned(UnresolvedObjectReference { + object_id: obj_to_stake.into(), + version: None, + digest: None, + }), + UnresolvedInputArgument::Pure { + value: bcs::to_bytes(&validator_address).unwrap(), + }, + ], + commands: vec![Command::MoveCall(sui_sdk_types::types::MoveCall { + package: "0x3".parse().unwrap(), + module: "sui_system".parse().unwrap(), + function: "request_add_stake".parse().unwrap(), + type_arguments: vec![], + arguments: vec![Argument::Input(0), Argument::Input(1), Argument::Input(2)], + })], + }, + sender: sender.into(), + gas_payment: None, + expiration: TransactionExpiration::None, + }; + + let resolved = client + .inner() + .resolve_transaction_with_parameters( + &unresolved_transaction, + &ResolveTransactionQueryParameters { + simulate: true, + ..Default::default() + }, + ) + .await + .unwrap() + .into_inner(); + + let signed_transaction = test_cluster + .wallet + .sign_transaction(&resolved.transaction.try_into().unwrap()); + let effects = client + .execute_transaction( + &ExecuteTransactionQueryParameters::default(), + &signed_transaction, + ) + .await + .unwrap() + .effects; + + assert!(effects.status().is_ok()); + assert_eq!( + resolved.simulation.unwrap().effects, + effects.try_into().unwrap() + ); +} + +#[sim_test] +async fn resolve_transaction_insufficient_gas() { + let test_cluster = TestClusterBuilder::new().build().await; + let client = Client::new(test_cluster.rpc_url()); + + // Test the case where we don't have enough coins/gas for the required budget + let unresolved_transaction = UnresolvedTransaction { + ptb: UnresolvedProgrammableTransaction { + inputs: vec![UnresolvedInputArgument::Shared { + object_id: "0x6".parse().unwrap(), + initial_shared_version: None, + mutable: None, + }], + commands: vec![Command::MoveCall(sui_sdk_types::types::MoveCall { + package: "0x2".parse().unwrap(), + module: "clock".parse().unwrap(), + function: "timestamp_ms".parse().unwrap(), + type_arguments: vec![], + arguments: vec![Argument::Input(0)], + })], + }, + sender: SuiAddress::random_for_testing_only().into(), // random account with no gas + gas_payment: None, + expiration: TransactionExpiration::None, + }; + + let error = client + .inner() + .resolve_transaction(&unresolved_transaction) + .await + .unwrap_err(); + + assert_eq!(error.status(), Some(StatusCode::BAD_REQUEST)); + assert_contains( + error.message().unwrap_or_default(), + "unable to select sufficient gas", + ); +} + +fn assert_contains(haystack: &str, needle: &str) { + if !haystack.contains(needle) { + panic!("{haystack:?} does not contain {needle:?}"); + } +} diff --git a/crates/sui-faucet/src/faucet/simple_faucet.rs b/crates/sui-faucet/src/faucet/simple_faucet.rs index ce20b669baae9..6bd0fda59e285 100644 --- a/crates/sui-faucet/src/faucet/simple_faucet.rs +++ b/crates/sui-faucet/src/faucet/simple_faucet.rs @@ -117,6 +117,9 @@ impl SimpleFaucet { .filter(|coin| coin.0.balance.value() >= (config.amount * config.num_coins as u64)) .collect::>(); let metrics = FaucetMetrics::new(prometheus_registry); + // set initial balance when faucet starts + let balance = coins.iter().map(|coin| coin.0.balance.value()).sum::(); + metrics.balance.set(balance as i64); let wal = WriteAheadLog::open(wal_path); let mut pending = vec![]; @@ -482,6 +485,17 @@ impl SimpleFaucet { } else { self.recycle_gas_coin(coin_id, uuid).await; } + + if let Some(ref balances) = result.balance_changes { + let sui_used = balances + .iter() + .find(|balance| balance.owner == self.active_address) + .map(|b| b.amount) + .unwrap_or_else(|| 0); + info!("SUI used in this tx {}: {}", tx_digest, sui_used); + self.metrics.balance.add(sui_used as i64); + } + Ok(result) } } @@ -618,11 +632,14 @@ impl SimpleFaucet { let tx_digest = tx.digest(); let client = self.wallet.get_client().await?; + Ok(client .quorum_driver_api() .execute_transaction_block( tx.clone(), - SuiTransactionBlockResponseOptions::new().with_effects(), + SuiTransactionBlockResponseOptions::new() + .with_effects() + .with_balance_changes(), Some(ExecuteTransactionRequestType::WaitForLocalExecution), ) .await diff --git a/crates/sui-faucet/src/metrics.rs b/crates/sui-faucet/src/metrics.rs index e67bbc334cd27..ee7184f3db0ba 100644 --- a/crates/sui-faucet/src/metrics.rs +++ b/crates/sui-faucet/src/metrics.rs @@ -24,6 +24,7 @@ pub struct RequestMetrics { /// Metrics relevant to the running of the service #[derive(Clone, Debug)] pub struct FaucetMetrics { + pub(crate) balance: IntGauge, pub(crate) current_executions_in_flight: IntGauge, pub(crate) total_available_coins: IntGauge, pub(crate) total_discarded_coins: IntGauge, @@ -88,6 +89,11 @@ impl RequestMetrics { impl FaucetMetrics { pub fn new(registry: &Registry) -> Self { Self { + balance: register_int_gauge_with_registry!( + "balance", + "Current balance of the all the available coins", + registry, + ).unwrap(), current_executions_in_flight: register_int_gauge_with_registry!( "current_executions_in_flight", "Current number of transactions being executed in Faucet", diff --git a/crates/sui-framework/src/lib.rs b/crates/sui-framework/src/lib.rs index f51f746585acb..dcfd9f9fd12d8 100644 --- a/crates/sui-framework/src/lib.rs +++ b/crates/sui-framework/src/lib.rs @@ -3,7 +3,6 @@ use move_binary_format::binary_config::BinaryConfig; use move_binary_format::compatibility::Compatibility; -use move_binary_format::file_format::{Ability, AbilitySet}; use move_binary_format::CompiledModule; use move_core_types::gas_algebra::InternalGas; use once_cell::sync::Lazy; @@ -223,23 +222,7 @@ pub async fn compare_system_package( return Some(cur_ref); } - let compatibility = Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - // Checking `entry` linkage is required because system packages are updated in-place, and a - // transaction that was rolled back to make way for reconfiguration should still be runnable - // after a reconfiguration that upgraded the framework. - // - // A transaction that calls a system function that was previously `entry` and is now private - // will fail because its entrypoint became no longer callable. A transaction that calls a - // system function that was previously `public entry` and is now just `public` could also - // fail if one of its mutable inputs was being used in another private `entry` function. - check_private_entry_linking: true, - disallowed_new_abilities: AbilitySet::singleton(Ability::Key), - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }; + let compatibility = Compatibility::framework_upgrade_check(); let new_pkg = new_object .data diff --git a/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.exp b/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.exp index cb95142a98918..54894fbd9b26d 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.exp @@ -1,4 +1,4 @@ -processed 13 tasks +processed 12 tasks init: A: object(0,0) @@ -31,7 +31,7 @@ task 5, line 60: //# create-checkpoint Checkpoint created: 1 -task 6, lines 62-85: +task 6, lines 62-118: //# run-graphql Response: { "data": { @@ -49,7 +49,14 @@ Response: { "bcs": "AA==" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AQAAAAAAAAA=", + "data": { + "Number": "1" + } } }, { @@ -63,7 +70,56 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { - "__typename": "MoveObject" + "__typename": "MoveObject", + "contents": { + "type": { + "repr": "0x314faae908e1e1e6f5df9f3a03a4692a27cb974776f13551472d3831f5a964d9::m::Child" + }, + "bcs": "79SK5tokongSfcWj2640StZoQwIktLuTsSSR/HV6Odc=", + "data": { + "Struct": [ + { + "name": "id", + "value": { + "UID": [ + 239, + 212, + 138, + 230, + 218, + 36, + 162, + 120, + 18, + 125, + 197, + 163, + 219, + 174, + 52, + 74, + 214, + 104, + 67, + 2, + 36, + 180, + 187, + 147, + 177, + 36, + 145, + 252, + 117, + 122, + 57, + 215 + ] + } + } + ] + } + } } }, { @@ -77,7 +133,14 @@ Response: { "bcs": "AA==" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AgAAAAAAAAA=", + "data": { + "Number": "2" + } } }, { @@ -91,31 +154,20 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AAAAAAAAAAA=", + "data": { + "Number": "0" + } } } ] } - } - } -} - -task 7, line 87: -//# run Test::m::wrap --sender A --args object(2,0) -created: object(7,0) -mutated: object(0,0) -wrapped: object(2,0) -gas summary: computation_cost: 1000000, storage_cost: 2485200, storage_rebate: 2212056, non_refundable_storage_fee: 22344 - -task 8, line 89: -//# create-checkpoint -Checkpoint created: 2 - -task 9, lines 91-114: -//# run-graphql -Response: { - "data": { - "object": { + }, + "owner": { "dynamicFields": { "nodes": [ { @@ -129,7 +181,14 @@ Response: { "bcs": "AA==" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AQAAAAAAAAA=", + "data": { + "Number": "1" + } } }, { @@ -143,7 +202,56 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { - "__typename": "MoveObject" + "__typename": "MoveObject", + "contents": { + "type": { + "repr": "0x314faae908e1e1e6f5df9f3a03a4692a27cb974776f13551472d3831f5a964d9::m::Child" + }, + "bcs": "79SK5tokongSfcWj2640StZoQwIktLuTsSSR/HV6Odc=", + "data": { + "Struct": [ + { + "name": "id", + "value": { + "UID": [ + 239, + 212, + 138, + 230, + 218, + 36, + 162, + 120, + 18, + 125, + 197, + 163, + 219, + 174, + 52, + 74, + 214, + 104, + 67, + 2, + 36, + 180, + 187, + 147, + 177, + 36, + 145, + 252, + 117, + 122, + 57, + 215 + ] + } + } + ] + } + } } }, { @@ -157,7 +265,14 @@ Response: { "bcs": "AA==" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AgAAAAAAAAA=", + "data": { + "Number": "2" + } } }, { @@ -171,7 +286,14 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { - "__typename": "MoveValue" + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, + "bcs": "AAAAAAAAAAA=", + "data": { + "Number": "0" + } } } ] @@ -180,10 +302,22 @@ Response: { } } -task 10, lines 116-141: +task 7, line 120: +//# run Test::m::wrap --sender A --args object(2,0) +created: object(7,0) +mutated: object(0,0) +wrapped: object(2,0) +gas summary: computation_cost: 1000000, storage_cost: 2485200, storage_rebate: 2212056, non_refundable_storage_fee: 22344 + +task 8, line 122: +//# create-checkpoint +Checkpoint created: 2 + +task 9, lines 124-180: //# run-graphql Response: { "data": { + "object": null, "owner": { "dynamicFields": { "nodes": [ @@ -198,11 +332,14 @@ Response: { "bcs": "AA==" }, "value": { + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, "bcs": "AQAAAAAAAAA=", "data": { "Number": "1" - }, - "__typename": "MoveValue" + } } }, { @@ -216,7 +353,56 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { - "__typename": "MoveObject" + "__typename": "MoveObject", + "contents": { + "type": { + "repr": "0x314faae908e1e1e6f5df9f3a03a4692a27cb974776f13551472d3831f5a964d9::m::Child" + }, + "bcs": "79SK5tokongSfcWj2640StZoQwIktLuTsSSR/HV6Odc=", + "data": { + "Struct": [ + { + "name": "id", + "value": { + "UID": [ + 239, + 212, + 138, + 230, + 218, + 36, + 162, + 120, + 18, + 125, + 197, + 163, + 219, + 174, + 52, + 74, + 214, + 104, + 67, + 2, + 36, + 180, + 187, + 147, + 177, + 36, + 145, + 252, + 117, + 122, + 57, + 215 + ] + } + } + ] + } + } } }, { @@ -230,11 +416,14 @@ Response: { "bcs": "AA==" }, "value": { + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, "bcs": "AgAAAAAAAAA=", "data": { "Number": "2" - }, - "__typename": "MoveValue" + } } }, { @@ -248,11 +437,14 @@ Response: { "bcs": "AAAAAAAAAAA=" }, "value": { + "__typename": "MoveValue", + "type": { + "repr": "u64" + }, "bcs": "AAAAAAAAAAA=", "data": { "Number": "0" - }, - "__typename": "MoveValue" + } } } ] @@ -261,7 +453,7 @@ Response: { } } -task 11, lines 143-163: +task 10, lines 182-201: //# run-graphql Response: { "data": { @@ -278,6 +470,9 @@ Response: { }, "value": { "__typename": "MoveValue", + "type": { + "repr": "u64" + }, "bcs": "AAAAAAAAAAA=", "data": { "Number": "0" @@ -288,14 +483,63 @@ Response: { } } -task 12, lines 165-176: +task 11, lines 203-219: //# run-graphql Response: { "data": { "owner": { "dynamicObjectField": { "value": { - "__typename": "MoveObject" + "__typename": "MoveObject", + "contents": { + "type": { + "repr": "0x314faae908e1e1e6f5df9f3a03a4692a27cb974776f13551472d3831f5a964d9::m::Child" + }, + "bcs": "79SK5tokongSfcWj2640StZoQwIktLuTsSSR/HV6Odc=", + "data": { + "Struct": [ + { + "name": "id", + "value": { + "UID": [ + 239, + 212, + 138, + 230, + 218, + 36, + 162, + 120, + 18, + 125, + 197, + 163, + 219, + 174, + 52, + 74, + 214, + 104, + 67, + 2, + 36, + 180, + 187, + 147, + 177, + 36, + 145, + 252, + 117, + 122, + 57, + 215 + ] + } + } + ] + } + } } } } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.move b/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.move index e9c5a46703ea8..f4113f39eb6a0 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/call/dynamic_fields.move @@ -60,23 +60,56 @@ module Test::m { //# create-checkpoint //# run-graphql -{ +{ # Initially, the parent object should be accessible directly and as an + # "Owner". object(address: "@{obj_2_0}") { dynamicFields { nodes { name { - type { - repr - } + type { repr } data bcs } value { + __typename + ... on MoveValue { + type { repr } + bcs + data + } ... on MoveObject { - __typename + contents { + type { repr } + bcs + data + } } + } + } + } + } + + owner(address: "@{obj_2_0}") { + dynamicFields { + nodes { + name { + type { repr } + data + bcs + } + value { + __typename ... on MoveValue { - __typename + type { repr } + bcs + data + } + ... on MoveObject { + contents { + type { repr } + bcs + data + } } } } @@ -89,50 +122,56 @@ module Test::m { //# create-checkpoint //# run-graphql -{ +{ # After it is wrapped, we can no longer fetch its latest version via `object` + # but we can still refer to it as an "Owner" and fetch its dynamic fields. object(address: "@{obj_2_0}") { dynamicFields { nodes { name { - type { - repr - } + type { repr } data bcs } value { - ... on MoveObject { - __typename - } + __typename ... on MoveValue { - __typename + type { repr } + bcs + data + } + ... on MoveObject { + contents { + type { repr } + bcs + data + } } } } } } -} -//# run-graphql -{ owner(address: "@{obj_2_0}") { dynamicFields { nodes { name { - type { - repr - } + type { repr } data bcs } value { - ... on MoveObject { - __typename - } + __typename ... on MoveValue { + type { repr } bcs data - __typename + } + ... on MoveObject { + contents { + type { repr } + bcs + data + } } } } @@ -145,15 +184,14 @@ module Test::m { owner(address: "@{obj_2_0}") { dynamicField(name: {type: "u64", bcs: "AAAAAAAAAAA="}) { name { - type { - repr - } + type { repr } data bcs } value { ... on MoveValue { __typename + type { repr } bcs data } @@ -169,6 +207,11 @@ module Test::m { value { ... on MoveObject { __typename + contents { + type { repr } + bcs + data + } } } } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/consistency/balances.move b/crates/sui-graphql-e2e-tests/tests/stable/consistency/balances.move index 2f524ac0e7d3c..d45a225849419 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/consistency/balances.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/consistency/balances.move @@ -78,7 +78,7 @@ module P0::fake { //# run-graphql --cursors {"c":2,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 2. Fake coin balance should be 700. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -101,7 +101,7 @@ module P0::fake { //# run-graphql --cursors {"c":3,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 3. Fake coin balance should be 500. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -124,7 +124,7 @@ module P0::fake { //# run-graphql --cursors {"c":4,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 4. Fake coin balance should be 400. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -151,7 +151,7 @@ module P0::fake { //# run-graphql --cursors {"c":2,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 2. Fake coin balance should be 700. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -174,7 +174,7 @@ module P0::fake { //# run-graphql --cursors {"c":3,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 3. Fake coin balance should be 500. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -197,7 +197,7 @@ module P0::fake { //# run-graphql --cursors {"c":4,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 4. Fake coin balance should be 400. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -236,7 +236,7 @@ module P0::fake { sequenceNumber } } - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -259,7 +259,7 @@ module P0::fake { //# run-graphql --cursors {"c":3,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 3. Fake coin balance should be 500. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -282,7 +282,7 @@ module P0::fake { //# run-graphql --cursors {"c":4,"t":1,"i":false} # Emulating viewing transaction blocks at checkpoint 4. Fake coin balance should be 400. { - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -329,7 +329,7 @@ module P0::fake { sequenceNumber } } - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -360,7 +360,7 @@ module P0::fake { sequenceNumber } } - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { @@ -391,7 +391,7 @@ module P0::fake { sequenceNumber } } - transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { nodes { sender { fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { diff --git a/crates/sui-graphql-e2e-tests/tests/stable/consistency/checkpoints/transaction_blocks.move b/crates/sui-graphql-e2e-tests/tests/stable/consistency/checkpoints/transaction_blocks.move index c505e86cccd0c..ef8a78bd82599 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/consistency/checkpoints/transaction_blocks.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/consistency/checkpoints/transaction_blocks.move @@ -56,7 +56,7 @@ module Test::M1 { checkpoints { nodes { sequenceNumber - transactionBlocks(filter: { signAddress: "@{A}"}) { + transactionBlocks(filter: { sentAddress: "@{A}"}) { edges { cursor node { diff --git a/crates/sui-graphql-e2e-tests/tests/stable/consistency/epochs/transaction_blocks.move b/crates/sui-graphql-e2e-tests/tests/stable/consistency/epochs/transaction_blocks.move index 425849aef9e16..f6e1f31b4777a 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/consistency/epochs/transaction_blocks.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/consistency/epochs/transaction_blocks.move @@ -249,7 +249,7 @@ module Test::M1 { checkpoint { sequenceNumber } - with_cursor: transactionBlocks(after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + with_cursor: transactionBlocks(after: "@{cursor_0}", filter: {sentAddress: "@{A}"}) { edges { cursor node { @@ -264,7 +264,7 @@ module Test::M1 { } } } - without_cursor: transactionBlocks(filter: {signAddress: "@{A}"}) { + without_cursor: transactionBlocks(filter: {sentAddress: "@{A}"}) { edges { cursor node { diff --git a/crates/sui-graphql-e2e-tests/tests/stable/consistency/object_at_version.exp b/crates/sui-graphql-e2e-tests/tests/stable/consistency/object_at_version.exp index 4cfc609655cfe..d45f731c7e1d6 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/consistency/object_at_version.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/consistency/object_at_version.exp @@ -93,11 +93,7 @@ task 10, lines 111-137: //# run-graphql Response: { "data": { - "latest_wrapped": { - "status": "WRAPPED_OR_DELETED", - "version": 5, - "asMoveObject": null - }, + "latest_wrapped": null, "previous_version": { "status": "INDEXED", "version": 4, @@ -140,11 +136,7 @@ Response: { } } }, - "previous_version": { - "status": "WRAPPED_OR_DELETED", - "version": 5, - "asMoveObject": null - }, + "previous_version": null, "first_version": { "status": "INDEXED", "version": 3, @@ -174,16 +166,8 @@ task 16, lines 187-213: //# run-graphql Response: { "data": { - "latest_deleted": { - "status": "WRAPPED_OR_DELETED", - "version": 7, - "asMoveObject": null - }, - "version_specified": { - "status": "WRAPPED_OR_DELETED", - "version": 7, - "asMoveObject": null - } + "latest_deleted": null, + "version_specified": null } } @@ -275,11 +259,7 @@ Response: { } } }, - "wrapped_or_deleted_object": { - "status": "WRAPPED_OR_DELETED", - "version": 5, - "asMoveObject": null - }, + "wrapped_or_deleted_object": null, "object_not_in_snapshot": { "status": "INDEXED", "version": 3, diff --git a/crates/sui-graphql-e2e-tests/tests/stable/consistency/tx_address_objects.move b/crates/sui-graphql-e2e-tests/tests/stable/consistency/tx_address_objects.move index 4f7bf42061ed0..e4dc118588e3b 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/consistency/tx_address_objects.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/consistency/tx_address_objects.move @@ -71,7 +71,7 @@ module Test::M1 { } } } - latest_tx_at_checkpoint_3: transactionBlocks(last: 1, filter: {signAddress: "@{A}"}) { + latest_tx_at_checkpoint_3: transactionBlocks(last: 1, filter: {sentAddress: "@{A}"}) { nodes { sender { objects_consistent_with_address_at_latest_checkpoint_4: objects(filter: {type: "@{Test}"}) { @@ -135,7 +135,7 @@ module Test::M1 { //# run-graphql { - all_transactions: transactionBlocks(first: 4, filter: {signAddress: "@{A}"}) { + all_transactions: transactionBlocks(first: 4, filter: {sentAddress: "@{A}"}) { nodes { sender { objects(filter: {type: "@{Test}"}) { @@ -195,7 +195,7 @@ module Test::M1 { } } } - latest_tx_at_checkpoint_3: transactionBlocks(last: 1, filter: {signAddress: "@{A}"}) { + latest_tx_at_checkpoint_3: transactionBlocks(last: 1, filter: {sentAddress: "@{A}"}) { nodes { sender { objects(filter: {type: "@{Test}"}) { @@ -262,7 +262,7 @@ module Test::M1 { //# run-graphql # Regardless of the transaction block, the nested fields should yield the same data. { - all_transactions: transactionBlocks(first: 4, filter: {signAddress: "@{A}"}) { + all_transactions: transactionBlocks(first: 4, filter: {sentAddress: "@{A}"}) { nodes { sender { objects(filter: {type: "@{Test}"}) { diff --git a/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.exp b/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.exp new file mode 100644 index 0000000000000..8086291fc3762 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.exp @@ -0,0 +1,194 @@ +processed 15 tasks + +init: +C: object(0,0) + +task 1, line 6: +//# advance-epoch +Epoch advanced: 0 + +task 2, line 8: +//# advance-epoch +Epoch advanced: 1 + +task 3, line 10: +//# advance-epoch +Epoch advanced: 2 + +task 4, line 12: +//# advance-epoch +Epoch advanced: 3 + +task 5, line 14: +//# advance-epoch +Epoch advanced: 4 + +task 6, line 16: +//# advance-epoch +Epoch advanced: 5 + +task 7, lines 18-29: +//# run-graphql +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false + }, + "nodes": [ + { + "epochId": 4 + }, + { + "epochId": 5 + } + ] + } + } +} + +task 8, lines 31-42: +//# run-graphql +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true + }, + "nodes": [ + { + "epochId": 0 + }, + { + "epochId": 1 + }, + { + "epochId": 2 + } + ] + } + } +} + +task 9, lines 44-55: +//# run-graphql --cursors {"c":5,"e":2} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true + }, + "nodes": [ + { + "epochId": 0 + }, + { + "epochId": 1 + } + ] + } + } +} + +task 10, lines 57-68: +//# run-graphql --cursors {"c":3,"e":4} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false + }, + "nodes": [ + { + "epochId": 0 + }, + { + "epochId": 1 + }, + { + "epochId": 2 + } + ] + } + } +} + +task 11, lines 70-81: +//# run-graphql --cursors {"c":11,"e":1} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false + }, + "nodes": [ + { + "epochId": 2 + }, + { + "epochId": 3 + }, + { + "epochId": 4 + }, + { + "epochId": 5 + }, + { + "epochId": 6 + } + ] + } + } +} + +task 12, lines 83-94: +//# run-graphql --cursors {"c":0,"e":5} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false + }, + "nodes": [ + { + "epochId": 0 + } + ] + } + } +} + +task 13, lines 96-107: +//# run-graphql --cursors {"c":3,"e":4} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false + }, + "nodes": [] + } + } +} + +task 14, lines 109-120: +//# run-graphql --cursors {"c":0,"e":0} +Response: { + "data": { + "epochs": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false + }, + "nodes": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.move b/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.move new file mode 100644 index 0000000000000..fea4bf3701126 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/stable/epoch/pagination.move @@ -0,0 +1,120 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --simulator --accounts C + +//# advance-epoch + +//# advance-epoch + +//# advance-epoch + +//# advance-epoch + +//# advance-epoch + +//# advance-epoch + +//# run-graphql +{ + epochs(last:2) { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql +{ + epochs(first:3) { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":5,"e":2} +{ + epochs(before: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":3,"e":4} +{ + epochs(before: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":11,"e":1} +{ + epochs(after: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":0,"e":5} +{ + epochs(before: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":3,"e":4} +{ + epochs(after: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} + +//# run-graphql --cursors {"c":0,"e":0} +{ + epochs(after: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + } + nodes { + epochId + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/stable/objects/wrap_unwrap.exp b/crates/sui-graphql-e2e-tests/tests/stable/objects/wrap_unwrap.exp index ba5edfc21f009..0d74390873867 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/objects/wrap_unwrap.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/objects/wrap_unwrap.exp @@ -46,9 +46,7 @@ Response: { "object1": { "digest": "4ZQVjpKKr9Et5qZNWvYQvpmydf2j7ohU1wB8bZ9LFKrG" }, - "object2": { - "digest": null - } + "object2": null } } @@ -77,9 +75,7 @@ task 10, lines 66-74: //# run-graphql Response: { "data": { - "object1": { - "digest": null - }, + "object1": null, "object2": { "digest": "jVRpzwEAGgYKYJ5rQfpW2zjMajazCAF9VVGUXEACpN1" } @@ -106,8 +102,6 @@ Response: { "object1": { "digest": "8B8PFHkbEB8w99ZevdLkb6CBuR2gZyPsE7zUWyXJZwDw" }, - "object2": { - "digest": null - } + "object2": null } } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.exp b/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.exp new file mode 100644 index 0000000000000..f0e668bcd3410 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.exp @@ -0,0 +1,23 @@ +processed 4 tasks + +task 1, lines 6-9: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 3442800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 11: +//# create-checkpoint +Checkpoint created: 1 + +task 3, lines 13-20: +//# run-graphql +Response: { + "data": { + "package": { + "bcs": "Adz1CN2mPNeBZVYbv5RqhGYkv1DQpl31VsxGSdfjr8viAQAAAAAAAAABAW1aoRzrCwYAAAAGAQACAwIFBQcDBwoECA4gDC4QAAEAAAABAAABAwFmAW3c9QjdpjzXgWVWG7+UaoRmJL9Q0KZd9VbMRknX46/L4gABAAAAAgYqAAAAAAAAAAIAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQAAAAAAAAADIDoneo4Y+M2qKottIwlwrhAoW7myDDdD4qGyoq6coLMGEHUlAAAAAAA=", + "packageBcs": "3PUI3aY814FlVhu/lGqEZiS/UNCmXfVWzEZJ1+Ovy+IBAAAAAAAAAAEBbVqhHOsLBgAAAAYBAAIDAgUFBwMHCgQIDiAMLhAAAQAAAAEAAAEDAWYBbdz1CN2mPNeBZVYbv5RqhGYkv1DQpl31VsxGSdfjr8viAAEAAAACBioAAAAAAAAAAgAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAAAAAAAAAA==", + "moduleBcs": "AQFtWqEc6wsGAAAABgEAAgMCBQUHAwcKBAgOIAwuEAABAAAAAQAAAQMBZgFt3PUI3aY814FlVhu/lGqEZiS/UNCmXfVWzEZJ1+Ovy+IAAQAAAAIGKgAAAAAAAAACAA==" + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.move b/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.move new file mode 100644 index 0000000000000..1f80bd62d0dde --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/stable/packages/bcs.move @@ -0,0 +1,20 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --addresses P=0x0 --simulator + +//# publish +module P::m { + public fun f(): u64 { 42 } +} + +//# create-checkpoint + +//# run-graphql +{ + package(address: "@{P}") { + bcs + packageBcs + moduleBcs + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.exp b/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.exp index 9f704b768ddde..681819a6ed0d7 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.exp @@ -1,4 +1,4 @@ -processed 10 tasks +processed 11 tasks init: A: object(0,0) @@ -297,3 +297,87 @@ Response: { } } } + +task 10, lines 122-138: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "nodes": [ + { + "digest": "FPhSSzT7tHmrPhs3H9GT1n4Dqj3eyCgaFLkQSc9FEDVV", + "effects": { + "events": { + "nodes": [] + } + } + }, + { + "digest": "GJMTYHH46d31ohELwH3ZfvGvbiDZ7GCqNcyg4fGGFJJQ", + "effects": { + "events": { + "nodes": [] + } + } + }, + { + "digest": "48LNnMV9MFXiXfXwDRWzu6SndwxY3ZfKUNEKJiDqJqM7", + "effects": { + "events": { + "nodes": [ + { + "transactionBlock": { + "digest": "48LNnMV9MFXiXfXwDRWzu6SndwxY3ZfKUNEKJiDqJqM7" + } + } + ] + } + } + }, + { + "digest": "FhpR5hSSP2wf8ABXKcDVVTzo7H1DZYUUgABWPtADHcQA", + "effects": { + "events": { + "nodes": [ + { + "transactionBlock": { + "digest": "FhpR5hSSP2wf8ABXKcDVVTzo7H1DZYUUgABWPtADHcQA" + } + }, + { + "transactionBlock": { + "digest": "FhpR5hSSP2wf8ABXKcDVVTzo7H1DZYUUgABWPtADHcQA" + } + } + ] + } + } + }, + { + "digest": "2CdTttmE8ciZggrK8qnyjgLaDoDNzT6UCzFsn4834UWX", + "effects": { + "events": { + "nodes": [ + { + "transactionBlock": { + "digest": "2CdTttmE8ciZggrK8qnyjgLaDoDNzT6UCzFsn4834UWX" + } + }, + { + "transactionBlock": { + "digest": "2CdTttmE8ciZggrK8qnyjgLaDoDNzT6UCzFsn4834UWX" + } + }, + { + "transactionBlock": { + "digest": "2CdTttmE8ciZggrK8qnyjgLaDoDNzT6UCzFsn4834UWX" + } + } + ] + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.move b/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.move index 1b4b739de1232..5eb35f3a6e6b9 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transaction_block_effects/events.move @@ -37,7 +37,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(filter: { signAddress: "@{A}" }) { + transactionBlocks(filter: { sentAddress: "@{A}" }) { nodes { effects{ events { @@ -75,7 +75,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 1, filter: { signAddress: "@{A}" }) { + transactionBlocks(first: 1, filter: { sentAddress: "@{A}" }) { nodes { effects { events(last: 1) { @@ -98,7 +98,7 @@ module Test::M1 { //# run-graphql --cursors {"i":0,"c":1} { - transactionBlocks(last: 1, filter: { signAddress: "@{A}" }) { + transactionBlocks(last: 1, filter: { sentAddress: "@{A}" }) { nodes { effects { events(first: 2, after: "@{cursor_0}") { @@ -118,3 +118,21 @@ module Test::M1 { } } } + +//# run-graphql +{ + transactionBlocks { + nodes { + digest + effects { + events { + nodes { + transactionBlock { + digest + } + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.exp b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.exp similarity index 84% rename from crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.exp rename to crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.exp index bd348488c7358..afa4dc65590a9 100644 --- a/crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.exp @@ -51,7 +51,7 @@ task 7, line 48: //# create-checkpoint Checkpoint created: 1 -task 8, lines 50-97: +task 8, lines 50-92: //# run-graphql Response: { "data": { @@ -563,146 +563,6 @@ Response: { } ] }, - "receivedByA": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - }, - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999992026120" - } - } - } - }, - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "2000000" - } - } - } - } - ] - } - } - }, - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999992026120" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999987028240" - } - } - } - }, - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "3000000" - } - } - } - } - ] - } - } - }, - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999987028240" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999981030360" - } - } - } - }, - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "4000000" - } - } - } - } - ] - } - } - } - ] - }, "affectsB": { "nodes": [ { diff --git a/crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.move similarity index 93% rename from crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.move rename to crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.move index 6ea97e1dfc69e..22b386346369d 100644 --- a/crates/sui-graphql-e2e-tests/tests/staging/transactions/filters/affected_address.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/affected_address.move @@ -65,11 +65,6 @@ nodes { ...CoinBalances } } - # ...and the transactions that A was a recipient for - receivedByA: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, recvAddress: "@{A}" }, scanLimit: 1000) { - nodes { ...CoinBalances } - } - # B was not affected by all the transactions affectsB: transactionBlocks(filter: { kind: PROGRAMMABLE_TX, affectedAddress: "@{B}" }, scanLimit: 1000) { nodes { ...CoinBalances } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/kind.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/kind.move index cc15acea59813..6b17cdd8c5dd9 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/kind.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/kind.move @@ -54,7 +54,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{A}"}) { + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 sentAddress: "@{A}"}) { pageInfo { hasNextPage hasPreviousPage @@ -74,7 +74,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{B}"}) { + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 sentAddress: "@{B}"}) { pageInfo { hasNextPage hasPreviousPage @@ -94,7 +94,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{C}"}) { + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 sentAddress: "@{C}"}) { pageInfo { hasNextPage hasPreviousPage @@ -114,7 +114,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{D}"}) { + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 sentAddress: "@{D}"}) { pageInfo { hasNextPage hasPreviousPage @@ -134,7 +134,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{E}"}) { + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 sentAddress: "@{E}"}) { pageInfo { hasNextPage hasPreviousPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.exp b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.exp index 03ca9cd780032..d1bc5b1a5a98d 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.exp +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.exp @@ -3,7 +3,7 @@ processed 5 tasks init: A: object(0,0), B: object(0,1) -task 1, lines 9-11: +task 1, lines 6-8: //# programmable --sender A --inputs 1000000 @B //> SplitCoins(Gas, [Input(0)]); //> TransferObjects([Result(0)], Input(1)) @@ -11,7 +11,7 @@ created: object(1,0) mutated: object(0,0) gas summary: computation_cost: 1000000, storage_cost: 1976000, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 13-15: +task 2, lines 10-12: //# programmable --sender B --inputs 2000000 @A //> SplitCoins(Gas, [Input(0)]); //> TransferObjects([Result(0)], Input(1)) @@ -19,11 +19,11 @@ created: object(2,0) mutated: object(0,1) gas summary: computation_cost: 1000000, storage_cost: 1976000, storage_rebate: 0, non_refundable_storage_fee: 0 -task 3, line 17: +task 3, line 14: //# create-checkpoint Checkpoint created: 1 -task 4, lines 19-87: +task 4, lines 16-46: //# run-graphql Response: { "data": { @@ -65,85 +65,6 @@ Response: { } ] }, - "bySignAddress": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - } - ] - }, - "bothAddresses": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - } - ] - }, - "differentAddresses": { - "nodes": [] - }, "compoundBySentAddress": { "nodes": [ { @@ -182,85 +103,6 @@ Response: { } ] }, - "compoundBySignAddress": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - } - ] - }, - "compoundBothAddresses": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - } - ] - }, - "compoundDifferentAddresses": { - "nodes": [] - }, "sentViaAddress": { "transactionBlocks": { "nodes": [ @@ -300,46 +142,6 @@ Response: { } ] } - }, - "signViaAddress": { - "transactionBlocks": { - "nodes": [ - { - "effects": { - "objectChanges": { - "nodes": [ - { - "inputState": null, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "1000000" - } - } - } - }, - { - "inputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "300000000000000" - } - } - }, - "outputState": { - "asMoveObject": { - "asCoin": { - "coinBalance": "299999996024000" - } - } - } - } - ] - } - } - } - ] - } } } } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.move index 0f779510891a0..283d3c3a13cc2 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/sent.move @@ -3,9 +3,6 @@ //# init --protocol-version 51 --addresses Test=0x0 --accounts A B --simulator -// Confirm that the new `sentAddress` behaves like `signAddress`, and how the -// two filters interact with each other. - //# programmable --sender A --inputs 1000000 @B //> SplitCoins(Gas, [Input(0)]); //> TransferObjects([Result(0)], Input(1)) @@ -22,53 +19,15 @@ query { nodes { ...CoinBalances } } - bySignAddress: transactionBlocks(filter: { signAddress: "@{A}" }) { - nodes { ...CoinBalances } - } - - bothAddresses: transactionBlocks(filter: { sentAddress: "@{A}", signAddress: "@{A}" }) { - nodes { ...CoinBalances } - } - - differentAddresses: transactionBlocks(filter: { sentAddress: "@{A}", signAddress: "@{B}" }) { - nodes { ...CoinBalances } - } - compoundBySentAddress: transactionBlocks(filter: { sentAddress: "@{A}", kind: PROGRAMMABLE_TX }) { nodes { ...CoinBalances } } - compoundBySignAddress: transactionBlocks(filter: { signAddress: "@{A}", kind: PROGRAMMABLE_TX }) { - nodes { ...CoinBalances } - } - - compoundBothAddresses: transactionBlocks(filter: { - sentAddress: "@{A}", - signAddress: "@{A}", - kind: PROGRAMMABLE_TX, - }) { - nodes { ...CoinBalances } - } - - compoundDifferentAddresses: transactionBlocks(filter: { - sentAddress: "@{A}", - signAddress: "@{B}", - kind: PROGRAMMABLE_TX, - }) { - nodes { ...CoinBalances } - } - sentViaAddress: address(address: "@{A}") { transactionBlocks(relation: SENT) { nodes { ...CoinBalances } } } - - signViaAddress: address(address: "@{A}") { - transactionBlocks(relation: SIGN) { - nodes { ...CoinBalances } - } - } } fragment CoinBalances on TransactionBlock { diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/transaction_ids.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/transaction_ids.move index a217465798b11..690fdb2503705 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/transaction_ids.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/filters/transaction_ids.move @@ -46,7 +46,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(filter: {signAddress: "@{A}" transactionIds: []}) { + transactionBlocks(filter: {sentAddress: "@{A}" transactionIds: []}) { pageInfo { hasNextPage hasPreviousPage @@ -66,7 +66,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(filter: {recvAddress: "@{A}" transactionIds: []}) { + transactionBlocks(filter: {affectedAddress: "@{A}" transactionIds: []}) { pageInfo { hasNextPage hasPreviousPage @@ -86,7 +86,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(scanLimit: 10 filter: {recvAddress: "@{A}" transactionIds: []}) { + transactionBlocks(scanLimit: 10 filter: {affectedAddress: "@{A}" transactionIds: []}) { pageInfo { hasNextPage hasPreviousPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/programmable.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/programmable.move index 39bba91810a74..af8c48664a9bf 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/programmable.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/programmable.move @@ -831,7 +831,7 @@ fragment ComprehensivePTB on ProgrammableTransactionBlock { //# run-graphql { # Conflicting filter and context address(address: "@{A}") { - transactionBlocks(last: 10, filter: { signAddress: "0x0" }) { + transactionBlocks(last: 10, filter: { sentAddress: "0x0" }) { nodes { kind { __typename } } } } diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/alternating.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/alternating.move index 8aaaba3b27c08..2c1232e6157b8 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/alternating.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/alternating.move @@ -56,7 +56,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 2 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -82,7 +82,7 @@ module Test::M1 { # so tx 4 and 6. the boundary cursors should wrap the response set, # and both should have isScanLimited set to false { - transactionBlocks(first: 2 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -107,7 +107,7 @@ module Test::M1 { # Meanwhile, because of the scanLimit of 2, the boundary cursors are # startCursor: 4, endCursor: 5, and both are scan limited { - transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -133,7 +133,7 @@ module Test::M1 { # startCursor: 7, endCursor: 8, both scan limited # response set consists of single tx 8 { - transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -162,7 +162,7 @@ module Test::M1 { # consequently, the endCursor wraps the result set # startCursor: 6, endCursor: 8, endCursor is not scan limited { - transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 6 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 6 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -187,7 +187,7 @@ module Test::M1 { # fetch the last tx without scan limit # startCursor = endCursor = 10, wrapping the response set { - transactionBlocks(first: 2 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -213,7 +213,7 @@ module Test::M1 { # unlike the not-scan-limited query, the start and end cursors # are expanded out to the scanned window, instead of wrapping the response set { - transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 6 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 after: "@{cursor_0}" scanLimit: 6 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/both_cursors.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/both_cursors.move index ab23179dbfa3e..68c126e25e24a 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/both_cursors.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/both_cursors.move @@ -59,7 +59,7 @@ module Test::M1 { # startCursor is at 3 + scanLimited, endCursor at 4 + not scanLimited # this is because between (2, 7), txs 4 and 6 match, and thus endCursor snaps to last of result { - transactionBlocks(first: 1 scanLimit: 4 after: "@{cursor_0}" before: "@{cursor_1}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 1 scanLimit: 4 after: "@{cursor_0}" before: "@{cursor_1}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -86,7 +86,7 @@ module Test::M1 { # and scan-limit logic will override this as there are still more txs to scan # note that we're scanning txs [3, 6] { - transactionBlocks(first: 3 scanLimit: 4 after: "@{cursor_0}" before: "@{cursor_1}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 3 scanLimit: 4 after: "@{cursor_0}" before: "@{cursor_1}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -111,7 +111,7 @@ module Test::M1 { # txs 5 and 7 match, but due to page size of `first: 1`, we only return tx 5 # startCursor is 5 + scan limited, endCursor is also 5 + scan limited { - transactionBlocks(first: 1 scanLimit: 3 after: "@{cursor_0}" before: "@{cursor_1}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 1 scanLimit: 3 after: "@{cursor_0}" before: "@{cursor_1}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/first.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/first.move index 997ebb0bb953f..0d6eeea40a29d 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/first.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/first.move @@ -60,7 +60,7 @@ module Test::M1 { # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] <- tx_sequence_number # [B, A, B, A, B, A, A, A, B, B] { - transactionBlocks(first: 50 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 50 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -89,7 +89,7 @@ module Test::M1 { # The cursor for the node will have `is_scan_limited` flag set to false, because we know for sure there is # a corresponding element for the cursor in the result set. { - transactionBlocks(first: 2 scanLimit: 2 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 scanLimit: 2 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -115,7 +115,7 @@ module Test::M1 { # Still paginating with `scanLimit`, both the start and end cursors should have `is_scan_limited` flag to true # because of the scanLimit of 4, startCursor = endCursor = 4 { - transactionBlocks(first: 1 scanLimit: 1 after: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 1 scanLimit: 1 after: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -143,7 +143,7 @@ module Test::M1 { # instead of wrapping around the result set, the boundary cursors are pushed out # to the first and last transaction scanned in this query { - transactionBlocks(first: 3 scanLimit: 3 after: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 3 scanLimit: 3 after: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -171,7 +171,7 @@ module Test::M1 { # instead of returninng None, we set the boundary cursors # to the first and last transaction scanned in this query { - transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -198,7 +198,7 @@ module Test::M1 { # startCursor at 10, endCursor at 11 # correctly detects we've reached the end of the upper bound { - transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -230,7 +230,7 @@ module Test::M1 { # regardless of the specified scanLimit # correctly detects we've reached the end of the upper bound { - transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 5}) { + transactionBlocks(first: 2 scanLimit: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 5}) { pageInfo { hasPreviousPage hasNextPage @@ -259,7 +259,7 @@ module Test::M1 { # there is a next page, which is the 12th tx, which should yield an empty set # per the filtering criteria { - transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 5}) { + transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 5}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/last.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/last.move index a37c7976d1fc1..d917b925e27dc 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/last.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/equal/last.move @@ -57,7 +57,7 @@ module Test::M1 { //# run-graphql # Expect ten results { - transactionBlocks(last: 50 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 50 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -84,7 +84,7 @@ module Test::M1 { # startCursor: 10, endCursor: 11 # result is single element with cursor: 11 { - transactionBlocks(last: 2 scanLimit: 2 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 2 scanLimit: 2 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -110,7 +110,7 @@ module Test::M1 { # startCursor: 9, endCursor: 9 # result is single element with cursor: 9 { - transactionBlocks(last: 1 scanLimit: 1 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 1 scanLimit: 1 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -136,7 +136,7 @@ module Test::M1 { # startCursor: 6, endCursor: 8 # result is single element with cursor: 7 { - transactionBlocks(last: 3 scanLimit: 3 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 scanLimit: 3 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -162,7 +162,7 @@ module Test::M1 { # startCursor: 4, endCursor: 5 # expect empty set { - transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -187,7 +187,7 @@ module Test::M1 { # Returns the first two matching transactions, boundary cursors both have `is_scan_limited: true` # startCursor: 2, endCursor: 3 { - transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -213,7 +213,7 @@ module Test::M1 { # Since we know from the previous query that there is not a previous page at this cursor, # Expect false for page flags and null for cursors { - transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/first.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/first.move index caac3936e49f2..a6113ca543cce 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/first.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/first.move @@ -78,7 +78,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -107,7 +107,7 @@ module Test::M1 { # this is so we don't end up skipping any other matches in the scan range # but beyond the scope of the `limit` { - transactionBlocks(first: 1 scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -133,7 +133,7 @@ module Test::M1 { # endCursor ends at 7, not 3, because we've exhausted all the matches # within the window of scanning range, and will overwrite the endCursor to 7. { - transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -158,7 +158,7 @@ module Test::M1 { # startCursor: 8, endCursor: 12, both are scan-limited # expect an empty set { - transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -183,7 +183,7 @@ module Test::M1 { # startCursor: 13, endCursor: 17, both are scan-limited # single element returned, coincidentally also the last scanned transaction { - transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -210,7 +210,7 @@ module Test::M1 { # but due to the `first` limit, we return a subset. # we don't want to skip over other matches, so we don't push the endCursor out { - transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -236,7 +236,7 @@ module Test::M1 { # single element returned, coincidentally also the last scanned transaction # note that the startCursor is 19, not 18 or 21, since we can use the scan-limited behavior { - transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(first: 1 after: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/last.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/last.move index ddbce16557c6a..c6211042f38e3 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/last.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/ge_page/last.move @@ -78,7 +78,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -103,7 +103,7 @@ module Test::M1 { //# run-graphql # startCursor 21 not scan limited, endCursor 21 is scan limited { - transactionBlocks(last: 1 scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -130,7 +130,7 @@ module Test::M1 { # `scanLimit` is 5 - if we set the `startCursor` to 17, then we will never yield tx 17 # when paginating the other way, since the cursors are exclusive. { - transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -155,7 +155,7 @@ module Test::M1 { # continuing paginating backwards with scan limit # startCursor 11, endCursor 15, both scan limited { - transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -179,7 +179,7 @@ module Test::M1 { //# run-graphql --cursors {"c":7,"t":11,"i":true} # startCursor is 7, endCursor is 10, both scan limited { - transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -205,7 +205,7 @@ module Test::M1 { # this is because we found a matching element at tx 3, but within # the scanned window there is another tx that we need to return for { - transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage @@ -229,7 +229,7 @@ module Test::M1 { //# run-graphql --cursors {"c":7,"t":3,"i":false} # Reached the end { - transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + transactionBlocks(last: 1 before: "@{cursor_0}" scanLimit: 5 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/invalid_limits.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/invalid_limits.move index bd8e0515508a5..4be8a80e255e8 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/invalid_limits.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/invalid_limits.move @@ -44,7 +44,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 0 scanLimit: 2 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 0 scanLimit: 2 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -67,7 +67,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 2 scanLimit: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 2 scanLimit: 0 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -90,7 +90,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 0 scanLimit: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 0 scanLimit: 0 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -113,7 +113,7 @@ module Test::M1 { //# run-graphql { - transactionBlocks(first: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 0 filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/first.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/first.move index c08e311648fc6..18f82f6a0d83e 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/first.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/first.move @@ -84,7 +84,7 @@ module Test::M1 { //# run-graphql # startCursor 2, endCursor 3, both scan limited { - transactionBlocks(first: 3 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 3 scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -108,7 +108,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":3,"i":true} # startCursor: 4, endCursor 5, both scan limited { - transactionBlocks(first: 3 scanLimit: 2 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 3 scanLimit: 2 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -136,7 +136,7 @@ module Test::M1 { # we set the endCursor to the final tx scanned, rather than snapping # to the last matched tx { - transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -160,7 +160,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":8,"i":true} # startCursor: 9, endCursor: 11 both scanLimited { - transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -185,7 +185,7 @@ module Test::M1 { # using the last element's cursor from the previous query # will yield an empty set, fixed on the last scannable tx { - transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -210,7 +210,7 @@ module Test::M1 { # trying to paginate on the `endCursor` even though hasNextPage is false # cursors are null, both page flags are false { - transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(first: 4 scanLimit: 3 after: "@{cursor_0}" filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/last.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/last.move index d6ee06795a67c..c78b67522f4bd 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/last.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/le_page/last.move @@ -83,7 +83,7 @@ module Test::M1 { //# run-graphql # startCursor: 10, endCursor: 11, both scan limited { - transactionBlocks(last: 3 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -107,7 +107,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":10,"i":true} # startCursor: 8, endCursor: 9, both scan limited { - transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -132,7 +132,7 @@ module Test::M1 { # use result's cursor instead of boundary cursor # startCursor: 6, endCursor: 7, both scan limited { - transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -156,7 +156,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":6,"i":true} # startCursor: 4, endCursor: 5, both scan limited { - transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -180,7 +180,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":4,"i":false} # reached the end with this query { - transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage @@ -205,7 +205,7 @@ module Test::M1 { //# run-graphql --cursors {"c":4,"t":2,"i":true} # cursors are null, and page flags are both false { - transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(last: 3 before: "@{cursor_0}" scanLimit: 2 filter: {affectedAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/require.move b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/require.move index a4b3e2249f3d0..c7de79724e4a1 100644 --- a/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/require.move +++ b/crates/sui-graphql-e2e-tests/tests/stable/transactions/scan_limit/require.move @@ -55,7 +55,7 @@ module Test::M1 { //# run-graphql # Expect ten results { - transactionBlocks(filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(filter: {affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasNextPage hasPreviousPage @@ -76,7 +76,7 @@ module Test::M1 { //# run-graphql # Don't need scanLimit with sender { - transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + transactionBlocks(filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { pageInfo { hasNextPage hasPreviousPage @@ -97,7 +97,7 @@ module Test::M1 { //# run-graphql # scanLimit required { - transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { + transactionBlocks(filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { pageInfo { hasNextPage hasPreviousPage @@ -118,7 +118,7 @@ module Test::M1 { //# run-graphql # valid { - transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { + transactionBlocks(scanLimit: 50 filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { pageInfo { hasNextPage hasPreviousPage @@ -139,7 +139,7 @@ module Test::M1 { //# run-graphql # scanLimit required { - transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { + transactionBlocks(filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { pageInfo { hasNextPage hasPreviousPage @@ -160,7 +160,7 @@ module Test::M1 { //# run-graphql # valid { - transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { + transactionBlocks(scanLimit: 50 filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { pageInfo { hasNextPage hasPreviousPage @@ -181,7 +181,7 @@ module Test::M1 { //# run-graphql # scanLimit required { - transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { + transactionBlocks(filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { pageInfo { hasNextPage hasPreviousPage @@ -202,7 +202,7 @@ module Test::M1 { //# run-graphql # valid { - transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { + transactionBlocks(scanLimit: 50 filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { pageInfo { hasNextPage hasPreviousPage @@ -224,7 +224,7 @@ module Test::M1 { //# run-graphql # scanLimit required { - transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 changedObject: "@{obj_3_0}"}) { + transactionBlocks(filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 changedObject: "@{obj_3_0}"}) { pageInfo { hasNextPage hasPreviousPage @@ -247,7 +247,7 @@ module Test::M1 { # Because scanLimit is specified, the boundary cursors should be at 2 and 11, # and both will indicate is_scan_limited { - transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 changedObject: "@{obj_3_0}"}) { + transactionBlocks(scanLimit: 50 filter: {sentAddress: "@{A}" affectedAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 changedObject: "@{obj_3_0}"}) { pageInfo { hasPreviousPage hasNextPage diff --git a/crates/sui-graphql-rpc/examples/checkpoint/with_tx_sent_addr_filter.graphql b/crates/sui-graphql-rpc/examples/checkpoint/with_tx_sent_addr_filter.graphql index 8d6ecbb50a000..8862c9d71ffdc 100644 --- a/crates/sui-graphql-rpc/examples/checkpoint/with_tx_sent_addr_filter.graphql +++ b/crates/sui-graphql-rpc/examples/checkpoint/with_tx_sent_addr_filter.graphql @@ -1,4 +1,4 @@ -# Select checkpoint at sequence number 14830285 for transactions from signAddress +# Select checkpoint at sequence number 14830285 for transactions from sentAddress { checkpoint(id: { sequenceNumber: 14830285 }) { digest @@ -6,7 +6,7 @@ timestamp transactionBlocks( filter: { - signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" + sentAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" } ) { edges { diff --git a/crates/sui-graphql-rpc/examples/transaction_block_connection/recv_addr_filter.graphql b/crates/sui-graphql-rpc/examples/transaction_block_connection/affected_addr_filter.graphql similarity index 89% rename from crates/sui-graphql-rpc/examples/transaction_block_connection/recv_addr_filter.graphql rename to crates/sui-graphql-rpc/examples/transaction_block_connection/affected_addr_filter.graphql index 97c690e22651f..61e6de3dfc36b 100644 --- a/crates/sui-graphql-rpc/examples/transaction_block_connection/recv_addr_filter.graphql +++ b/crates/sui-graphql-rpc/examples/transaction_block_connection/affected_addr_filter.graphql @@ -1,4 +1,4 @@ -# Filter on recvAddress +# Filter on affected address { transactionBlocks( filter: { diff --git a/crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sign_addr_filter.graphql b/crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sent_addr_filter.graphql similarity index 86% rename from crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sign_addr_filter.graphql rename to crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sent_addr_filter.graphql index ca0bb88bea535..5cf8f6675e845 100644 --- a/crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sign_addr_filter.graphql +++ b/crates/sui-graphql-rpc/examples/transaction_block_connection/input_object_sent_addr_filter.graphql @@ -3,7 +3,7 @@ transactionBlocks( filter: { inputObject: "0x0000000000000000000000000000000000000000000000000000000000000006" - signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" + sentAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" } ) { nodes { diff --git a/crates/sui-graphql-rpc/examples/transaction_block_connection/sign_addr_filter.graphql b/crates/sui-graphql-rpc/examples/transaction_block_connection/sent_addr_filter.graphql similarity index 77% rename from crates/sui-graphql-rpc/examples/transaction_block_connection/sign_addr_filter.graphql rename to crates/sui-graphql-rpc/examples/transaction_block_connection/sent_addr_filter.graphql index a15a4056e4a8b..e5692d42b351d 100644 --- a/crates/sui-graphql-rpc/examples/transaction_block_connection/sign_addr_filter.graphql +++ b/crates/sui-graphql-rpc/examples/transaction_block_connection/sent_addr_filter.graphql @@ -2,7 +2,7 @@ { transactionBlocks( filter: { - signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" + sentAddress: "0x0000000000000000000000000000000000000000000000000000000000000000" } ) { nodes { diff --git a/crates/sui-graphql-rpc/schema.graphql b/crates/sui-graphql-rpc/schema.graphql index 3f7177c446b7d..92330bc77ffa1 100644 --- a/crates/sui-graphql-rpc/schema.graphql +++ b/crates/sui-graphql-rpc/schema.graphql @@ -163,26 +163,15 @@ type AddressOwner { The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { - """ - Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - `SENT` which behaves identically but is named more clearly. Both filters restrict - transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - SIGN """ Transactions this address has sent. """ SENT """ - Transactions that sent objects to this address. NOTE: this input filter has been deprecated - in favor of `AFFECTED`, which offers an easier to understand behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `AFFECTED` is introduced, whichever is later. + Transactions that this address was involved in, either as the sender, sponsor, or as the + owner of some object that was created, modified or transfered. """ - RECV + AFFECTED } """ @@ -1200,7 +1189,42 @@ type Epoch { transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } +type EpochConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EpochEdge!]! + """ + A list of nodes. + """ + nodes: [Epoch!]! +} + +""" +An edge in a connection. +""" +type EpochEdge { + """ + The item at the end of the edge + """ + node: Epoch! + """ + A cursor for use in pagination + """ + cursor: String! +} + type Event { + """ + The transaction block that emitted this event. This information is only available for + events from indexed transactions, and not from transactions that have just been executed or + dry-run. + """ + transactionBlock: TransactionBlock """ The Move module containing some function that when called by a programmable transaction block (PTB) emitted this event. @@ -2335,6 +2359,10 @@ type MovePackage implements IObject & IOwner { """ typeOrigins: [TypeOrigin!] """ + BCS representation of the package itself, as a MovePackage. + """ + packageBcs: Base64 + """ BCS representation of the package's modules. Modules appear as a sequence of pairs (module name, followed by module bytes), in alphabetic order by module name. """ @@ -2872,11 +2900,6 @@ enum ObjectKind { The object is fetched from the index. """ INDEXED - """ - The object is deleted or wrapped and only partial information can be loaded from the - indexer. - """ - WRAPPED_OR_DELETED } """ @@ -3052,11 +3075,13 @@ type PageInfo { """ If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -the dynamic field, or the intermediate Field object itself). Also note that if the owner -is a parent, then it's guaranteed to be an object. +the dynamic field, or the intermediate Field object itself), and it is owned by another object. + +Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +object could be wrapped and therefore not directly accessible. """ type Parent { - parent: Object + parent: Owner } """ @@ -3287,6 +3312,7 @@ type Query { `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + epochs(first: Int, after: String, last: Int, before: String): EpochConnection! """ The checkpoints that exist in the network. """ @@ -4184,7 +4210,7 @@ type TransactionBlock { """ expiration: Epoch """ - Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. """ bcs: Base64 } @@ -4307,27 +4333,15 @@ input TransactionBlockFilter { """ beforeCheckpoint: UInt53 """ - Limit to transactions that were sent by the given address. NOTE: this input filter has been - deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - Both filters restrict transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). + Limit to transactions that interacted with the given address. The address could be a + sender, sponsor, or recipient of the transaction. """ - signAddress: SuiAddress + affectedAddress: SuiAddress """ Limit to transactions that were sent by the given address. """ sentAddress: SuiAddress """ - Limit to transactions that sent an object to the given address. NOTE: this input filter has - been deprecated in favor of `affectedAddress` which offers an easier to understand - behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `affectedAddress` is introduced, whichever is later. - """ - recvAddress: SuiAddress - """ Limit to transactions that accepted the given object as an input. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to under behavior. diff --git a/crates/sui-graphql-rpc/src/consistency.rs b/crates/sui-graphql-rpc/src/consistency.rs index 285e6ce8f36ba..1bf0650edef8c 100644 --- a/crates/sui-graphql-rpc/src/consistency.rs +++ b/crates/sui-graphql-rpc/src/consistency.rs @@ -68,10 +68,11 @@ impl ScanLimited for JsonCursor {} /// The `objects_snapshot` table contains the latest versions of objects up to a checkpoint sequence /// number, and `objects_history` captures changes after that, so a query to both tables is /// necessary to handle these object states: -/// 1) In snapshot, not in history - occurs when an object gets snapshotted and then has not been +/// 1) In snapshot, not in history - occurs when a live object gets snapshotted and then has not been /// modified since -/// 2) In history, not in snapshot - occurs when a new object is created -/// 3) In snapshot and in history - occurs when an object is snapshotted and further modified +/// 2) Not in snapshot, in history - occurs when a new object is created or a wrapped object is unwrapped +/// 3) In snapshot and in history - occurs when an object is snapshotted and further modified, the modification +/// can be wrapping or deleting. /// /// Additionally, even among objects that satisfy the filtering criteria, it is possible that there /// is a yet more recent version of the object within the checkpoint range, such as when the owner @@ -146,6 +147,7 @@ pub(crate) fn build_objects_query( // Similar to the snapshot query, construct the filtered inner query for the history table. let mut history_objs_inner = query!("SELECT * FROM objects_history"); history_objs_inner = filter_fn(history_objs_inner); + history_objs_inner = filter!(history_objs_inner, "object_status = 0"); let mut history_objs = match view { View::Consistent => { diff --git a/crates/sui-graphql-rpc/src/types/address.rs b/crates/sui-graphql-rpc/src/types/address.rs index 3d5be6cd28256..323a9138a2057 100644 --- a/crates/sui-graphql-rpc/src/types/address.rs +++ b/crates/sui-graphql-rpc/src/types/address.rs @@ -28,23 +28,10 @@ pub(crate) struct Address { /// The possible relationship types for a transaction block: sent, or received. #[derive(Enum, Copy, Clone, Eq, PartialEq)] pub(crate) enum AddressTransactionBlockRelationship { - /// Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - /// `SENT` which behaves identically but is named more clearly. Both filters restrict - /// transactions by their sender, only, not signers in general. - /// - /// This filter will be removed with 1.36.0 (2024-10-14). - Sign, /// Transactions this address has sent. Sent, - /// Transactions that sent objects to this address. NOTE: this input filter has been deprecated - /// in favor of `AFFECTED`, which offers an easier to understand behavior. - /// - /// This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - /// `AFFECTED` is introduced, whichever is later. - Recv, /// Transactions that this address was involved in, either as the sender, sponsor, or as the /// owner of some object that was created, modified or transfered. - #[cfg(feature = "staging")] Affected, } @@ -186,17 +173,11 @@ impl Address { let Some(filter) = filter.unwrap_or_default().intersect(match relation { // Relationship defaults to "sent" if none is supplied. - Some(R::Sign) | Some(R::Sent) | None => TransactionBlockFilter { + Some(R::Sent) | None => TransactionBlockFilter { sent_address: Some(self.address), ..Default::default() }, - Some(R::Recv) => TransactionBlockFilter { - recv_address: Some(self.address), - ..Default::default() - }, - - #[cfg(feature = "staging")] Some(R::Affected) => TransactionBlockFilter { affected_address: Some(self.address), ..Default::default() diff --git a/crates/sui-graphql-rpc/src/types/coin.rs b/crates/sui-graphql-rpc/src/types/coin.rs index fdc63dd611e81..f911256f477f4 100644 --- a/crates/sui-graphql-rpc/src/types/coin.rs +++ b/crates/sui-graphql-rpc/src/types/coin.rs @@ -174,8 +174,8 @@ impl Coin { } /// The owner type of this object: Immutable, Shared, Parent, Address - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_.super_).owner().await } /// The transaction block that created this version of the object. diff --git a/crates/sui-graphql-rpc/src/types/coin_metadata.rs b/crates/sui-graphql-rpc/src/types/coin_metadata.rs index 2052c8b42b03d..d12f7f343ec77 100644 --- a/crates/sui-graphql-rpc/src/types/coin_metadata.rs +++ b/crates/sui-graphql-rpc/src/types/coin_metadata.rs @@ -162,8 +162,8 @@ impl CoinMetadata { } /// The owner type of this object: Immutable, Shared, Parent, Address - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_.super_).owner().await } /// The transaction block that created this version of the object. diff --git a/crates/sui-graphql-rpc/src/types/dynamic_field.rs b/crates/sui-graphql-rpc/src/types/dynamic_field.rs index fdc77d4af77bc..ed6891a0c07e8 100644 --- a/crates/sui-graphql-rpc/src/types/dynamic_field.rs +++ b/crates/sui-graphql-rpc/src/types/dynamic_field.rs @@ -253,12 +253,6 @@ impl TryFrom for DynamicField { ObjectKind::NotIndexed(native) | ObjectKind::Indexed(native, _) => native.clone(), ObjectKind::Serialized(bytes) => bcs::from_bytes(bytes) .map_err(|e| Error::Internal(format!("Failed to deserialize object: {e}")))?, - - ObjectKind::WrappedOrDeleted => { - return Err(Error::Internal( - "DynamicField is wrapped or deleted.".to_string(), - )); - } }; let Some(object) = native.data.try_as_move() else { diff --git a/crates/sui-graphql-rpc/src/types/epoch.rs b/crates/sui-graphql-rpc/src/types/epoch.rs index 36839af4001ae..ab205f3b1c2f3 100644 --- a/crates/sui-graphql-rpc/src/types/epoch.rs +++ b/crates/sui-graphql-rpc/src/types/epoch.rs @@ -4,14 +4,15 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use crate::connection::ScanConnection; +use crate::consistency::Checkpointed; use crate::context_data::db_data_provider::{convert_to_validators, PgManager}; -use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; +use crate::data::{self, DataLoader, Db, DbConnection, QueryExecutor}; use crate::error::Error; use crate::server::watermark_task::Watermark; use super::big_int::BigInt; use super::checkpoint::{self, Checkpoint}; -use super::cursor::Page; +use super::cursor::{self, Page, Paginated, ScanLimited, Target}; use super::date_time::DateTime; use super::protocol_config::ProtocolConfigs; use super::system_state_summary::SystemStateSummary; @@ -21,9 +22,11 @@ use super::validator_set::ValidatorSet; use async_graphql::connection::Connection; use async_graphql::dataloader::Loader; use async_graphql::*; +use connection::{CursorType, Edge}; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::scoped_futures::ScopedFutureExt; use fastcrypto::encoding::{Base58, Encoding}; +use serde::{Deserialize, Serialize}; use sui_indexer::models::epoch::QueryableEpochInfo; use sui_indexer::schema::epochs; use sui_types::messages_checkpoint::CheckpointCommitment as EpochCommitment; @@ -42,6 +45,21 @@ struct EpochKey { pub checkpoint_viewed_at: u64, } +pub(crate) type Cursor = cursor::JsonCursor; +type Query = data::Query; + +/// The cursor returned for each `Epoch` in a connection's page of results. The +/// `checkpoint_viewed_at` will set the consistent upper bound for subsequent queries made on this +/// cursor. +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] +pub(crate) struct EpochCursor { + /// The checkpoint sequence number this was viewed at. + #[serde(rename = "c")] + pub checkpoint_viewed_at: u64, + #[serde(rename = "e")] + pub epoch_id: u64, +} + /// Operation of the Sui network is temporally partitioned into non-overlapping epochs, /// and the network aims to keep epochs roughly the same duration as each other. /// During a particular epoch the following data is fixed: @@ -342,8 +360,90 @@ impl Epoch { checkpoint_viewed_at, })) } + + pub(crate) async fn paginate( + db: &Db, + page: Page, + checkpoint_viewed_at: u64, + ) -> Result, Error> { + use epochs::dsl; + let cursor_viewed_at = page.validate_cursor_consistency()?; + let checkpoint_viewed_at = cursor_viewed_at.unwrap_or(checkpoint_viewed_at); + + let (prev, next, results) = db + .execute(move |conn| { + async move { + page.paginate_query::( + conn, + checkpoint_viewed_at, + move || { + dsl::epochs + .select(QueryableEpochInfo::as_select()) + .filter(dsl::first_checkpoint_id.le(checkpoint_viewed_at as i64)) + .into_boxed() + }, + ) + .await + } + .scope_boxed() + }) + .await?; + + // The "checkpoint viewed at" sets a consistent upper bound for the nested queries. + let mut conn = Connection::new(prev, next); + for stored in results { + let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); + conn.edges.push(Edge::new( + cursor, + Epoch { + stored, + checkpoint_viewed_at, + }, + )); + } + + Ok(conn) + } } +impl Paginated for QueryableEpochInfo { + type Source = epochs::table; + + fn filter_ge(cursor: &Cursor, query: Query) -> Query { + query.filter(epochs::dsl::epoch.ge(cursor.epoch_id as i64)) + } + + fn filter_le(cursor: &Cursor, query: Query) -> Query { + query.filter(epochs::dsl::epoch.le(cursor.epoch_id as i64)) + } + + fn order(asc: bool, query: Query) -> Query { + use epochs::dsl; + if asc { + query.order(dsl::epoch) + } else { + query.order(dsl::epoch.desc()) + } + } +} + +impl Target for QueryableEpochInfo { + fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { + Cursor::new(EpochCursor { + checkpoint_viewed_at, + epoch_id: self.epoch as u64, + }) + } +} + +impl Checkpointed for Cursor { + fn checkpoint_viewed_at(&self) -> u64 { + self.checkpoint_viewed_at + } +} + +impl ScanLimited for Cursor {} + #[async_trait::async_trait] impl Loader for Db { type Value = Epoch; diff --git a/crates/sui-graphql-rpc/src/types/event/mod.rs b/crates/sui-graphql-rpc/src/types/event/mod.rs index f478ebf3866a5..d5efd9fb259cb 100644 --- a/crates/sui-graphql-rpc/src/types/event/mod.rs +++ b/crates/sui-graphql-rpc/src/types/event/mod.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use super::cursor::{Page, Target}; use super::{ address::Address, base64::Base64, date_time::DateTime, move_module::MoveModule, - move_value::MoveValue, + move_value::MoveValue, transaction_block::TransactionBlock, }; use crate::data::{self, DbConnection, QueryExecutor}; use crate::query; @@ -50,6 +50,22 @@ type Query = data::Query; #[Object] impl Event { + /// The transaction block that emitted this event. This information is only available for + /// events from indexed transactions, and not from transactions that have just been executed or + /// dry-run. + async fn transaction_block(&self, ctx: &Context<'_>) -> Result> { + let Some(stored) = &self.stored else { + return Ok(None); + }; + + TransactionBlock::query( + ctx, + TransactionBlock::by_seq(stored.tx_sequence_number as u64, self.checkpoint_viewed_at), + ) + .await + .extend() + } + /// The Move module containing some function that when called by /// a programmable transaction block (PTB) emitted this event. /// For example, if a PTB invokes A::m1::foo, which internally diff --git a/crates/sui-graphql-rpc/src/types/move_object.rs b/crates/sui-graphql-rpc/src/types/move_object.rs index 5cb47571a8ab9..b78ef3dac54ec 100644 --- a/crates/sui-graphql-rpc/src/types/move_object.rs +++ b/crates/sui-graphql-rpc/src/types/move_object.rs @@ -242,8 +242,8 @@ impl MoveObject { } /// The owner type of this object: Immutable, Shared, Parent, Address - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_).owner().await } /// The transaction block that created this version of the object. diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index 6a8b0573cb154..ff4762f529a79 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -32,7 +32,7 @@ use diesel::prelude::QueryableByName; use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, Selectable}; use diesel_async::scoped_futures::ScopedFutureExt; use serde::{Deserialize, Serialize}; -use sui_indexer::models::objects::StoredHistoryObject; +use sui_indexer::models::objects::StoredFullHistoryObject; use sui_indexer::schema::packages; use sui_package_resolver::{error::Error as PackageCacheError, Package as ParsedMovePackage}; use sui_types::is_system_package; @@ -123,9 +123,10 @@ struct TypeOrigin { #[derive(Selectable, QueryableByName)] #[diesel(table_name = packages)] struct StoredHistoryPackage { + checkpoint_sequence_number: i64, original_id: Vec, #[diesel(embed)] - object: StoredHistoryObject, + object: StoredFullHistoryObject, } pub(crate) struct MovePackageDowncastError; @@ -136,6 +137,10 @@ pub(crate) type Cursor = BcsCursor; /// The inner struct for the `MovePackage` cursor. The package is identified by the checkpoint it /// was created in, its original ID, and its version, and the `checkpoint_viewed_at` specifies the /// checkpoint snapshot that the data came from. +/// +/// The cursor includes the checkpoint the package was created in as well, so that when we paginate +/// through all the packages on-chain, if we pause half way through, we can pick back up based on +/// the checkpoint we've seen so far. #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] pub(crate) struct PackageCursor { pub checkpoint_sequence_number: u64, @@ -308,8 +313,8 @@ impl MovePackage { /// The owner type of this object: Immutable, Shared, Parent, Address /// Packages are always Immutable. - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_).owner().await } /// The transaction block that published or upgraded this package. @@ -543,6 +548,17 @@ impl MovePackage { Some(type_origins) } + /// BCS representation of the package itself, as a MovePackage. + async fn package_bcs(&self) -> Result> { + let bcs = bcs::to_bytes(&self.native) + .map_err(|_| { + Error::Internal(format!("Failed to serialize package {}", self.native.id())) + }) + .extend()?; + + Ok(Some(bcs.into())) + } + /// BCS representation of the package's modules. Modules appear as a sequence of pairs (module /// name, followed by module bytes), in alphabetic order by module name. async fn module_bcs(&self) -> Result> { @@ -711,26 +727,26 @@ impl MovePackage { async move { let mut q = query!( r#" - SELECT - p.original_id, - o.* - FROM - packages p - INNER JOIN - objects_history o - ON - p.package_id = o.object_id - AND p.package_version = o.object_version - AND p.checkpoint_sequence_number = o.checkpoint_sequence_number - "# + SELECT + p.checkpoint_sequence_number, + p.original_id, + o.* + FROM + packages p + INNER JOIN + full_objects_history o + ON + p.package_id = o.object_id + AND p.package_version = o.object_version + "# ); q = filter!( q, - format!("o.checkpoint_sequence_number < {before_checkpoint}") + format!("p.checkpoint_sequence_number < {before_checkpoint}") ); if let Some(after) = after_checkpoint { - q = filter!(q, format!("{after} < o.checkpoint_sequence_number")); + q = filter!(q, format!("{after} < p.checkpoint_sequence_number")); } page.paginate_raw_query::(conn, checkpoint_viewed_at, q) @@ -745,8 +761,7 @@ impl MovePackage { // The "checkpoint viewed at" sets a consistent upper bound for the nested queries. for stored in results { let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); - let package = - MovePackage::try_from_stored_history_object(stored.object, checkpoint_viewed_at)?; + let package = MovePackage::try_from_serialized(stored.object, checkpoint_viewed_at)?; conn.edges.push(Edge::new(cursor, package)); } @@ -798,8 +813,7 @@ impl MovePackage { // The "checkpoint viewed at" sets a consistent upper bound for the nested queries. for stored in results { let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); - let package = - MovePackage::try_from_stored_history_object(stored.object, checkpoint_viewed_at)?; + let package = MovePackage::try_from_serialized(stored.object, checkpoint_viewed_at)?; conn.edges.push(Edge::new(cursor, package)); } @@ -809,15 +823,20 @@ impl MovePackage { /// `checkpoint_viewed_at` points to the checkpoint snapshot that this `MovePackage` came from. /// This is stored in the `MovePackage` so that related fields from the package are read from /// the same checkpoint (consistently). - pub(crate) fn try_from_stored_history_object( - history_object: StoredHistoryObject, + pub(crate) fn try_from_serialized( + history_object: StoredFullHistoryObject, checkpoint_viewed_at: u64, ) -> Result { - let object = Object::try_from_stored_history_object( - history_object, + let object = Object::new_serialized( + SuiAddress::from_bytes(&history_object.object_id) + .map_err(|_| Error::Internal("Invalid package ID".to_string()))?, + history_object.object_version as u64, + history_object.serialized_object, checkpoint_viewed_at, - /* root_version */ None, - )?; + history_object.object_version as u64, + ) + .ok_or_else(|| Error::Internal("Not a package!".to_string()))?; + Self::try_from(&object).map_err(|_| Error::Internal("Not a package!".to_string())) } } @@ -833,12 +852,12 @@ impl RawPaginated for StoredHistoryPackage { filter!( query, format!( - "o.checkpoint_sequence_number > {cp} OR (\ - o.checkpoint_sequence_number = {cp} AND - original_id > '\\x{id}'::bytea OR (\ - original_id = '\\x{id}'::bytea AND \ - o.object_version >= {pv}\ - ))", + "(p.checkpoint_sequence_number > {cp} OR (\ + p.checkpoint_sequence_number = {cp} AND \ + (original_id > '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + object_version >= {pv}\ + ))))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), pv = cursor.package_version, @@ -850,12 +869,12 @@ impl RawPaginated for StoredHistoryPackage { filter!( query, format!( - "o.checkpoint_sequence_number < {cp} OR (\ - o.checkpoint_sequence_number = {cp} AND - original_id < '\\x{id}'::bytea OR (\ - original_id = '\\x{id}'::bytea AND \ - o.object_version <= {pv}\ - ))", + "(p.checkpoint_sequence_number < {cp} OR (\ + p.checkpoint_sequence_number = {cp} AND \ + (original_id < '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + object_version <= {pv}\ + ))))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), pv = cursor.package_version, @@ -866,14 +885,14 @@ impl RawPaginated for StoredHistoryPackage { fn order(asc: bool, query: RawQuery) -> RawQuery { if asc { query - .order_by("o.checkpoint_sequence_number ASC") - .order_by("original_id ASC") - .order_by("o.object_version ASC") + .order_by("1 ASC") // checkpoint_sequence_number + .order_by("2 ASC") // original_id + .order_by("object_version ASC") } else { query - .order_by("o.checkpoint_sequence_number DESC") - .order_by("original_id DESC") - .order_by("o.object_version DESC") + .order_by("1 DESC") // checkpoint_sequence_number + .order_by("2 DESC") // original_id + .order_by("object_version DESC") } } } @@ -881,7 +900,7 @@ impl RawPaginated for StoredHistoryPackage { impl Target for StoredHistoryPackage { fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { Cursor::new(PackageCursor { - checkpoint_sequence_number: self.object.checkpoint_sequence_number as u64, + checkpoint_sequence_number: self.checkpoint_sequence_number as u64, original_id: self.original_id.clone(), package_version: self.object.object_version as u64, checkpoint_viewed_at, @@ -1049,7 +1068,11 @@ impl TryFrom<&Object> for MovePackage { } /// Query for fetching all the versions of a system package (assumes that `package` has already been -/// verified as a system package). This is an `objects_history` query disguised as a package query. +/// verified as a system package). This is a `full_objects_history` query disguised as a package query. +/// +/// We do this because the `packages` table contains only one entry per package ID. For the system +/// packages, this is the latest version of the package (for user packages, there is only one entry +/// per package ID anyway as each version of a package gets its own ID). fn system_package_version_query( package: SuiAddress, filter: Option, @@ -1058,35 +1081,42 @@ fn system_package_version_query( let mut q = query!( r#" SELECT - o.object_id AS original_id, + p.checkpoint_sequence_number, + p.original_id, o.* - FROM - objects_version v + FROM ( + SELECT + object_id AS package_id, + object_id AS original_id, + object_version AS package_version, + cp_sequence_number AS checkpoint_sequence_number + FROM + objects_version + ) p LEFT JOIN - objects_history o + full_objects_history o ON - v.object_id = o.object_id - AND v.object_version = o.object_version - AND v.cp_sequence_number = o.checkpoint_sequence_number + p.package_id = o.object_id + AND p.package_version = o.object_version "# ); q = filter!( q, format!( - "v.object_id = '\\x{}'::bytea", + "original_id = '\\x{}'::bytea", hex::encode(package.into_vec()) ) ); if let Some(after) = filter.as_ref().and_then(|f| f.after_version) { let a: u64 = after.into(); - q = filter!(q, format!("v.object_version > {a}")); + q = filter!(q, format!("object_version > {a}")); } if let Some(before) = filter.as_ref().and_then(|f| f.before_version) { let b: u64 = before.into(); - q = filter!(q, format!("v.object_version < {b}")); + q = filter!(q, format!("object_version < {b}")); } q @@ -1101,20 +1131,19 @@ fn user_package_version_query( let mut q = query!( r#" SELECT + p.checkpoint_sequence_number, p.original_id, o.* FROM packages q INNER JOIN packages p - ON - q.original_id = p.original_id + USING (original_id) INNER JOIN - objects_history o + full_objects_history o ON p.package_id = o.object_id AND p.package_version = o.object_version - AND p.checkpoint_sequence_number = o.checkpoint_sequence_number "# ); diff --git a/crates/sui-graphql-rpc/src/types/move_registry/named_type.rs b/crates/sui-graphql-rpc/src/types/move_registry/named_type.rs index 544642eafb5a1..9a522897ce560 100644 --- a/crates/sui-graphql-rpc/src/types/move_registry/named_type.rs +++ b/crates/sui-graphql-rpc/src/types/move_registry/named_type.rs @@ -9,7 +9,7 @@ use futures::future; use regex::{Captures, Regex}; use sui_types::{base_types::ObjectID, TypeTag}; -use crate::error::Error; +use crate::{data::package_resolver::PackageResolver, error::Error}; use super::{ error::MoveRegistryError, @@ -28,6 +28,7 @@ impl NamedType { name: &str, checkpoint_viewed_at: u64, ) -> Result { + let resolver: &PackageResolver = ctx.data_unchecked(); // we do not de-duplicate the names here, as the dataloader will do this for us. let names = Self::parse_names(name)?; @@ -55,7 +56,13 @@ impl NamedType { let correct_type_tag: String = Self::replace_names(name, &name_package_id_mapping)?; - TypeTag::from_str(&correct_type_tag).map_err(|e| Error::Client(format!("bad type: {e}"))) + let tag = TypeTag::from_str(&correct_type_tag) + .map_err(|e| Error::Client(format!("bad type: {e}")))?; + + resolver + .canonical_type(tag) + .await + .map_err(|e| Error::Internal(format!("Failed to retrieve type: {e}"))) } /// Is this already caught by the global limits? diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index 1b4b6c7e054c1..b7e538fc185a5 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -51,6 +51,7 @@ use sui_types::object::{ MoveObject as NativeMoveObject, Object as NativeObject, Owner as NativeOwner, }; use sui_types::TypeTag; + #[derive(Clone, Debug)] pub(crate) struct Object { pub address: SuiAddress, @@ -85,9 +86,6 @@ pub(crate) enum ObjectKind { Indexed(NativeObject, StoredHistoryObject), /// An object in the bcs serialized form. Serialized(Vec), - /// The object is wrapped or deleted and only partial information can be loaded from the - /// indexer. - WrappedOrDeleted, } #[derive(Enum, Copy, Clone, Eq, PartialEq, Debug)] @@ -98,9 +96,6 @@ pub enum ObjectStatus { NotIndexed, /// The object is fetched from the index. Indexed, - /// The object is deleted or wrapped and only partial information can be loaded from the - /// indexer. - WrappedOrDeleted, } #[derive(Clone, Debug, PartialEq, Eq, InputObject)] @@ -169,11 +164,13 @@ pub(crate) struct Shared { } /// If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -/// the dynamic field, or the intermediate Field object itself). Also note that if the owner -/// is a parent, then it's guaranteed to be an object. +/// the dynamic field, or the intermediate Field object itself), and it is owned by another object. +/// +/// Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +/// object could be wrapped and therefore not directly accessible. #[derive(SimpleObject, Clone)] pub(crate) struct Parent { - parent: Option, + parent: Option, } /// An address-owned object is owned by a specific 32-byte address that is @@ -445,8 +442,8 @@ impl Object { /// The owner type of this object: Immutable, Shared, Parent, Address /// Immutable and Shared Objects do not have owners. - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(self).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(self).owner().await } /// The transaction block that created this version of the object. @@ -586,7 +583,7 @@ impl ObjectImpl<'_> { .map(|native| native.digest().base58_encode()) } - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { + pub(crate) async fn owner(&self) -> Option { use NativeOwner as O; let native = self.0.native_impl()?; @@ -604,16 +601,14 @@ impl ObjectImpl<'_> { } O::Immutable => Some(ObjectOwner::Immutable(Immutable { dummy: None })), O::ObjectOwner(address) => { - let parent = Object::query( - ctx, - address.into(), - Object::latest_at(self.0.checkpoint_viewed_at), - ) - .await - .ok() - .flatten(); - - Some(ObjectOwner::Parent(Parent { parent })) + let address = SuiAddress::from(address); + Some(ObjectOwner::Parent(Parent { + parent: Some(Owner { + address, + checkpoint_viewed_at: self.0.checkpoint_viewed_at, + root_version: Some(self.0.root_version()), + }), + })) } O::Shared { initial_shared_version, @@ -632,9 +627,12 @@ impl ObjectImpl<'_> { }; let digest = native.previous_transaction; - TransactionBlock::query(ctx, digest.into(), self.0.checkpoint_viewed_at) - .await - .extend() + TransactionBlock::query( + ctx, + TransactionBlock::by_digest(digest.into(), self.0.checkpoint_viewed_at), + ) + .await + .extend() } pub(crate) async fn storage_rebate(&self) -> Option { @@ -658,9 +656,6 @@ impl ObjectImpl<'_> { let Some(filter) = filter .unwrap_or_default() .intersect(TransactionBlockFilter { - #[cfg(not(feature = "staging"))] - recv_address: Some(self.0.address), - #[cfg(feature = "staging")] affected_address: Some(self.0.address), ..Default::default() }) @@ -676,12 +671,7 @@ impl ObjectImpl<'_> { pub(crate) async fn bcs(&self) -> Result> { use ObjectKind as K; Ok(match &self.0.kind { - K::WrappedOrDeleted => None, - // WrappedOrDeleted objects are also read from the historical objects table, and they do - // not have a serialized object, so the column is also nullable for stored historical - // objects. K::Indexed(_, stored) => stored.serialized_object.as_ref().map(Base64::from), - K::NotIndexed(native) => { let bytes = bcs::to_bytes(native) .map_err(|e| { @@ -764,24 +754,14 @@ impl Object { serialized: Option>, checkpoint_viewed_at: u64, root_version: u64, - ) -> Self { - if let Some(bytes) = serialized { - Self { - address: object_id, - version, - kind: ObjectKind::Serialized(bytes), - checkpoint_viewed_at, - root_version, - } - } else { - Self { - address: object_id, - version, - kind: ObjectKind::WrappedOrDeleted, - checkpoint_viewed_at, - root_version: version, - } - } + ) -> Option { + serialized.map(|bytes| Self { + address: object_id, + version, + kind: ObjectKind::Serialized(bytes), + checkpoint_viewed_at, + root_version, + }) } pub(crate) fn native_impl(&self) -> Option { @@ -790,7 +770,6 @@ impl Object { match &self.kind { K::NotIndexed(native) | K::Indexed(native, _) => Some(native.clone()), K::Serialized(bytes) => bcs::from_bytes(bytes).ok(), - K::WrappedOrDeleted => None, } } @@ -1016,13 +995,9 @@ impl Object { root_version, }) } - NativeObjectStatus::WrappedOrDeleted => Ok(Self { - address, - version: history_object.object_version as u64, - kind: ObjectKind::WrappedOrDeleted, - checkpoint_viewed_at, - root_version: history_object.object_version as u64, - }), + NativeObjectStatus::WrappedOrDeleted => Err(Error::Internal( + "Wrapped or deleted objects should not be loaded from DB.".to_string(), + )), } } } @@ -1325,16 +1300,14 @@ impl Loader for Db { .zip(point_lookup_keys) .filter_map(|(hist_key, lookup_key)| { let object = objects.get(&lookup_key)?; - Some(( - *hist_key, - Object::new_serialized( - lookup_key.id, - lookup_key.version, - object.clone(), - hist_key.checkpoint_viewed_at, - lookup_key.version, - ), - )) + let hist_obj = Object::new_serialized( + lookup_key.id, + lookup_key.version, + object.clone(), + hist_key.checkpoint_viewed_at, + lookup_key.version, + ); + hist_obj.map(|obj| (*hist_key, obj)) }) .collect(); Ok(results) @@ -1435,18 +1408,16 @@ impl Loader for Db { .zip(point_lookup_keys) .filter_map(|(parent_key, lookup_key)| { let object = objects.get(&lookup_key)?; - Some(( - parent_key, - Object::new_serialized( - parent_key.id, - lookup_key.version, - object.clone(), - parent_key.checkpoint_viewed_at, - // If `ParentVersionKey::parent_version` is set, it must have been correctly - // propagated from the `Object::root_version` of some object. - parent_key.parent_version, - ), - )) + let hist_obj = Object::new_serialized( + parent_key.id, + lookup_key.version, + object.clone(), + parent_key.checkpoint_viewed_at, + // If `ParentVersionKey::parent_version` is set, it must have been correctly + // propagated from the `Object::root_version` of some object. + parent_key.parent_version, + ); + hist_obj.map(|obj| (parent_key, obj)) }) .collect(); @@ -1608,7 +1579,6 @@ impl From<&ObjectKind> for ObjectStatus { match kind { ObjectKind::NotIndexed(_) => ObjectStatus::NotIndexed, ObjectKind::Indexed(_, _) | ObjectKind::Serialized(_) => ObjectStatus::Indexed, - ObjectKind::WrappedOrDeleted => ObjectStatus::WrappedOrDeleted, } } } diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index ca8d9afc5a0f0..74d17363a0c31 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -29,7 +29,7 @@ use super::{ cursor::Page, digest::Digest, dry_run_result::DryRunResult, - epoch::Epoch, + epoch::{self, Epoch}, event::{self, Event, EventFilter}, move_type::MoveType, object::{self, Object, ObjectFilter}, @@ -323,9 +323,8 @@ impl Query { digest: Digest, ) -> Result> { let Watermark { checkpoint, .. } = *ctx.data()?; - TransactionBlock::query(ctx, digest, checkpoint) - .await - .extend() + let lookup = TransactionBlock::by_digest(digest, checkpoint); + TransactionBlock::query(ctx, lookup).await.extend() } /// The coin objects that exist in the network. @@ -356,6 +355,23 @@ impl Query { .extend() } + // The epochs of the network + async fn epochs( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + Epoch::paginate(ctx.data_unchecked(), page, checkpoint) + .await + .extend() + } + /// The checkpoints that exist in the network. async fn checkpoints( &self, diff --git a/crates/sui-graphql-rpc/src/types/stake.rs b/crates/sui-graphql-rpc/src/types/stake.rs index 75c0a07dabf5a..74c33726c29dc 100644 --- a/crates/sui-graphql-rpc/src/types/stake.rs +++ b/crates/sui-graphql-rpc/src/types/stake.rs @@ -181,8 +181,8 @@ impl StakedSui { } /// The owner type of this object: Immutable, Shared, Parent, Address - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_.super_).owner().await } /// The transaction block that created this version of the object. diff --git a/crates/sui-graphql-rpc/src/types/suins_registration.rs b/crates/sui-graphql-rpc/src/types/suins_registration.rs index f3dfa9c368e54..d8e9bb27dc142 100644 --- a/crates/sui-graphql-rpc/src/types/suins_registration.rs +++ b/crates/sui-graphql-rpc/src/types/suins_registration.rs @@ -219,8 +219,8 @@ impl SuinsRegistration { } /// The owner type of this object: Immutable, Shared, Parent, Address - pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { - ObjectImpl(&self.super_.super_).owner(ctx).await + pub(crate) async fn owner(&self) -> Option { + ObjectImpl(&self.super_.super_).owner().await } /// The transaction block that created this version of the object. diff --git a/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs b/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs index 7619d5fdb50f5..7af2c700a56d6 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs @@ -28,7 +28,6 @@ pub(crate) struct TransactionBlockFilter { /// Limit to transactions that interacted with the given address. The address could be a /// sender, sponsor, or recipient of the transaction. - #[cfg(feature = "staging")] pub affected_address: Option, /// Limit to transactions that interacted with the given object. The object could have been @@ -38,24 +37,9 @@ pub(crate) struct TransactionBlockFilter { #[cfg(feature = "staging")] pub affected_object: Option, - /// Limit to transactions that were sent by the given address. NOTE: this input filter has been - /// deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - /// Both filters restrict transactions by their sender, only, not signers in general. - /// - /// This filter will be removed with 1.36.0 (2024-10-14). - pub sign_address: Option, - /// Limit to transactions that were sent by the given address. pub sent_address: Option, - /// Limit to transactions that sent an object to the given address. NOTE: this input filter has - /// been deprecated in favor of `affectedAddress` which offers an easier to understand - /// behavior. - /// - /// This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - /// `affectedAddress` is introduced, whichever is later. - pub recv_address: Option, - /// Limit to transactions that accepted the given object as an input. NOTE: this input filter /// has been deprecated in favor of `affectedObject` which offers an easier to under behavior. /// @@ -94,13 +78,10 @@ impl TransactionBlockFilter { at_checkpoint: intersect!(at_checkpoint, intersect::by_eq)?, before_checkpoint: intersect!(before_checkpoint, intersect::by_min)?, - #[cfg(feature = "staging")] affected_address: intersect!(affected_address, intersect::by_eq)?, #[cfg(feature = "staging")] affected_object: intersect!(affected_object, intersect::by_eq)?, - sign_address: intersect!(sign_address, intersect::by_eq)?, sent_address: intersect!(sent_address, intersect::by_eq)?, - recv_address: intersect!(recv_address, intersect::by_eq)?, input_object: intersect!(input_object, intersect::by_eq)?, changed_object: intersect!(changed_object, intersect::by_eq)?, @@ -119,11 +100,9 @@ impl TransactionBlockFilter { [ self.function.is_some(), self.kind.is_some(), - #[cfg(feature = "staging")] self.affected_address.is_some(), #[cfg(feature = "staging")] self.affected_object.is_some(), - self.recv_address.is_some(), self.input_object.is_some(), self.changed_object.is_some(), self.transaction_ids.is_some(), @@ -140,17 +119,15 @@ impl TransactionBlockFilter { pub(crate) fn explicit_sender(&self) -> Option { let missing_implicit_sender = self.function.is_none() && self.kind.is_none() - && self.recv_address.is_none() + && self.affected_address.is_none() && self.input_object.is_none() && self.changed_object.is_none(); #[cfg(feature = "staging")] - let missing_implicit_sender = missing_implicit_sender - && self.affected_address.is_none() - && self.affected_object.is_none(); + let missing_implicit_sender = missing_implicit_sender && self.affected_object.is_none(); missing_implicit_sender - .then_some(self.sent_address.or(self.sign_address)) + .then_some(self.sent_address) .flatten() } @@ -159,16 +136,14 @@ impl TransactionBlockFilter { pub(crate) fn has_filters(&self) -> bool { let has_filters = self.function.is_some() || self.kind.is_some() - || self.sign_address.is_some() || self.sent_address.is_some() - || self.recv_address.is_some() + || self.affected_address.is_some() || self.input_object.is_some() || self.changed_object.is_some() || self.transaction_ids.is_some(); #[cfg(feature = "staging")] - let has_filters = - has_filters || self.affected_address.is_some() || self.affected_object.is_some(); + let has_filters = has_filters || self.affected_object.is_some(); has_filters } @@ -194,11 +169,5 @@ impl TransactionBlockFilter { if (kind == TransactionBlockKindInput::SystemTx) != (signer == SuiAddress::from(NativeSuiAddress::ZERO)) ) - // Temporary while we deprecate `sign_address` in favor of `sent_address`. - || matches!( - (self.sign_address, self.sent_address), - (Some(signer), Some(sent)) - if signer != sent - ) } } diff --git a/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs b/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs index b03a9e3cd7cd9..7bef4cf389438 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs @@ -93,6 +93,18 @@ pub(crate) enum TransactionBlockKindInput { ProgrammableTx = 1, } +/// Filter for a point query of a TransactionBlock. +pub(crate) enum TransactionBlockLookup { + ByDigest { + digest: Digest, + checkpoint_viewed_at: u64, + }, + BySeq { + tx_sequence_number: u64, + checkpoint_viewed_at: u64, + }, +} + type Query = data::Query; /// The cursor returned for each `TransactionBlock` in a connection's page of results. The @@ -110,14 +122,22 @@ pub(crate) struct TransactionBlockCursor { pub tx_checkpoint_number: u64, } -/// `DataLoader` key for fetching a `TransactionBlock` by its digest, optionally constrained by a -/// consistency cursor. +/// `DataLoader` key for fetching a `TransactionBlock` by its digest, constrained by a consistency +/// cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct DigestKey { pub digest: Digest, pub checkpoint_viewed_at: u64, } +/// `DataLoader` key for fetching a `TransactionBlock` by its sequence number, constrained by a +/// consistency cursor. +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +struct SeqKey { + pub tx_sequence_number: u64, + pub checkpoint_viewed_at: u64, +} + #[Object] impl TransactionBlock { /// A 32-byte hash that uniquely identifies the transaction block contents, encoded in Base58. @@ -198,17 +218,18 @@ impl TransactionBlock { .extend() } - /// Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + /// Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. async fn bcs(&self) -> Option { match &self.inner { - TransactionBlockInner::Stored { stored_tx, .. } => { - Some(Base64::from(&stored_tx.raw_transaction)) + TransactionBlockInner::Stored { native, .. } => Some(Base64::from( + &bcs::to_bytes(native.transaction_data()).unwrap(), + )), + TransactionBlockInner::Executed { tx_data, .. } => Some(Base64::from( + &bcs::to_bytes(tx_data.transaction_data()).unwrap(), + )), + TransactionBlockInner::DryRun { tx_data, .. } => { + Some(Base64::from(&bcs::to_bytes(tx_data).unwrap())) } - TransactionBlockInner::Executed { tx_data, .. } => { - bcs::to_bytes(&tx_data).ok().map(Base64::from) - } - // Dry run transaction does not have signatures so no sender signed data. - TransactionBlockInner::DryRun { .. } => None, } } } @@ -230,21 +251,60 @@ impl TransactionBlock { } } + /// Look-up the transaction block by its transaction digest. + pub(crate) fn by_digest(digest: Digest, checkpoint_viewed_at: u64) -> TransactionBlockLookup { + TransactionBlockLookup::ByDigest { + digest, + checkpoint_viewed_at, + } + } + + /// Look-up the transaction block by its sequence number (this is not usually exposed through + /// the GraphQL schema, but internally, othe entities in the DB will refer to transactions at + /// their sequence number). + pub(crate) fn by_seq( + tx_sequence_number: u64, + checkpoint_viewed_at: u64, + ) -> TransactionBlockLookup { + TransactionBlockLookup::BySeq { + tx_sequence_number, + checkpoint_viewed_at, + } + } + /// Look up a `TransactionBlock` in the database, by its transaction digest. Treats it as if it /// is being viewed at the `checkpoint_viewed_at` (e.g. the state of all relevant addresses will /// be at that checkpoint). pub(crate) async fn query( ctx: &Context<'_>, - digest: Digest, - checkpoint_viewed_at: u64, + lookup: TransactionBlockLookup, ) -> Result, Error> { let DataLoader(loader) = ctx.data_unchecked(); - loader - .load_one(DigestKey { + + match lookup { + TransactionBlockLookup::ByDigest { digest, checkpoint_viewed_at, - }) - .await + } => { + loader + .load_one(DigestKey { + digest, + checkpoint_viewed_at, + }) + .await + } + TransactionBlockLookup::BySeq { + tx_sequence_number, + checkpoint_viewed_at, + } => { + loader + .load_one(SeqKey { + tx_sequence_number, + checkpoint_viewed_at, + }) + .await + } + } } /// Look up multiple `TransactionBlock`s by their digests. Returns a map from those digests to @@ -278,8 +338,8 @@ impl TransactionBlock { /// If the `Page` is set, then this function will defer to the `checkpoint_viewed_at` in /// the cursor if they are consistent. /// - /// Filters that involve a combination of `recvAddress`, `inputObject`, `changedObject`, and - /// `function` should provide a value for `scan_limit`. This modifies querying behavior by + /// Filters that involve a combination of `affectedAddress`, `inputObject`, `changedObject`, + /// and `function` should provide a value for `scan_limit`. This modifies querying behavior by /// limiting how many transactions to scan through before applying filters, and also affects /// pagination behavior. pub(crate) async fn paginate( @@ -497,6 +557,63 @@ impl Loader for Db { } } +#[async_trait::async_trait] +impl Loader for Db { + type Value = TransactionBlock; + type Error = Error; + + async fn load(&self, keys: &[SeqKey]) -> Result, Error> { + use transactions::dsl as tx; + + let seqs: Vec<_> = keys.iter().map(|k| k.tx_sequence_number as i64).collect(); + + let transactions: Vec = self + .execute(move |conn| { + async move { + conn.results(move || { + tx::transactions + .select(StoredTransaction::as_select()) + .filter(tx::tx_sequence_number.eq_any(seqs.clone())) + }) + .await + } + .scope_boxed() + }) + .await + .map_err(|e| Error::Internal(format!("Failed to fetch transactions: {e}")))?; + + let seq_to_stored: BTreeMap<_, _> = transactions + .into_iter() + .map(|tx| (tx.tx_sequence_number as u64, tx)) + .collect(); + + let mut results = HashMap::new(); + for key in keys { + let Some(stored) = seq_to_stored.get(&key.tx_sequence_number).cloned() else { + continue; + }; + + // Filter by key's checkpoint viewed at here. Doing this in memory because it should be + // quite rare that this query actually filters something, but encoding it in SQL is + // complicated. + if key.checkpoint_viewed_at < stored.checkpoint_sequence_number as u64 { + continue; + } + + let inner = TransactionBlockInner::try_from(stored)?; + results.insert( + *key, + TransactionBlock { + inner, + checkpoint_viewed_at: key.checkpoint_viewed_at, + }, + ); + } + + Ok(results) + } +} + impl TryFrom for TransactionBlockInner { type Error = Error; diff --git a/crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs b/crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs index 9577be7a19340..46941617a56bd 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs @@ -305,7 +305,7 @@ fn min_option(xs: impl IntoIterator>) -> Option { /// Constructs a `RawQuery` as a join over all relevant side tables, filtered on their own filter /// condition, plus optionally a sender, plus optionally tx/cp bounds. pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) -> Option { - let sender = filter.sent_address.or(filter.sign_address); + let sender = filter.sent_address; let mut subqueries = vec![]; @@ -328,7 +328,6 @@ pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) - subqueries.push(("tx_kinds", select_kind(*kind, sender, tx_bounds))); } - #[cfg(feature = "staging")] if let Some(affected) = &filter.affected_address { subqueries.push(( "tx_affected_addresses", @@ -344,10 +343,6 @@ pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) - )); } - if let Some(recv) = &filter.recv_address { - subqueries.push(("tx_recipients", select_recipient(recv, sender, tx_bounds))); - } - if let Some(input) = &filter.input_object { subqueries.push(("tx_input_objects", select_input(input, sender, tx_bounds))); } @@ -360,7 +355,10 @@ pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) - } if let Some(sender) = &filter.explicit_sender() { - subqueries.push(("tx_senders", select_sender(sender, tx_bounds))); + subqueries.push(( + "tx_affected_addresses", + select_affected_address(sender, Some(*sender), tx_bounds), + )); } if let Some(txs) = &filter.transaction_ids { @@ -442,19 +440,21 @@ fn select_fun( /// Returns a RawQuery that selects transactions of a specific kind. If SystemTX is specified, we /// ignore the `sender`. If ProgrammableTX is specified, we filter against the `tx_kinds` table if -/// no `sender` is provided; otherwise, we just query the `tx_senders` table. Other combinations, in -/// particular when kind is SystemTx and sender is specified and not 0x0, are inconsistent and will -/// not produce any results. These inconsistent cases are expected to be checked for before this is -/// called. +/// no `sender` is provided; otherwise, we just query the `tx_affected_addresses` table. Other +/// combinations, in particular when kind is SystemTx and sender is specified and not 0x0, are +/// inconsistent and will not produce any results. These inconsistent cases are expected to be +/// checked for before this is called. fn select_kind( kind: TransactionBlockKindInput, sender: Option, bound: TxBounds, ) -> RawQuery { match (kind, sender) { - // We can simplify the query to just the `tx_senders` table if ProgrammableTX and sender is - // specified. - (TransactionBlockKindInput::ProgrammableTx, Some(sender)) => select_sender(&sender, bound), + // We can simplify the query to just the `tx_affected_addresses` table if ProgrammableTX + // and sender is specified. + (TransactionBlockKindInput::ProgrammableTx, Some(sender)) => { + select_affected_address(&sender, Some(sender), bound) + } // Otherwise, we can ignore the sender always, and just query the `tx_kinds` table. _ => filter!( select_tx(None, bound, "tx_kinds"), @@ -463,7 +463,6 @@ fn select_kind( } } -#[cfg(feature = "staging")] fn select_affected_address( affected: &SuiAddress, sender: Option, @@ -487,17 +486,6 @@ fn select_affected_object( ) } -fn select_sender(sender: &SuiAddress, bound: TxBounds) -> RawQuery { - select_tx(Some(*sender), bound, "tx_senders") -} - -fn select_recipient(recv: &SuiAddress, sender: Option, bound: TxBounds) -> RawQuery { - filter!( - select_tx(sender, bound, "tx_recipients"), - format!("recipient = {}", bytea_literal(recv.as_slice())) - ) -} - fn select_input(input: &SuiAddress, sender: Option, bound: TxBounds) -> RawQuery { filter!( select_tx(sender, bound, "tx_input_objects"), diff --git a/crates/sui-graphql-rpc/staging.graphql b/crates/sui-graphql-rpc/staging.graphql index ba8ba3b6a5f97..d37152f34e1bb 100644 --- a/crates/sui-graphql-rpc/staging.graphql +++ b/crates/sui-graphql-rpc/staging.graphql @@ -163,27 +163,11 @@ type AddressOwner { The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { - """ - Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - `SENT` which behaves identically but is named more clearly. Both filters restrict - transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - SIGN """ Transactions this address has sent. """ SENT """ - Transactions that sent objects to this address. NOTE: this input filter has been deprecated - in favor of `AFFECTED`, which offers an easier to understand behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `AFFECTED` is introduced, whichever is later. - """ - RECV - """ Transactions that this address was involved in, either as the sender, sponsor, or as the owner of some object that was created, modified or transfered. """ @@ -1205,7 +1189,42 @@ type Epoch { transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } +type EpochConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EpochEdge!]! + """ + A list of nodes. + """ + nodes: [Epoch!]! +} + +""" +An edge in a connection. +""" +type EpochEdge { + """ + The item at the end of the edge + """ + node: Epoch! + """ + A cursor for use in pagination + """ + cursor: String! +} + type Event { + """ + The transaction block that emitted this event. This information is only available for + events from indexed transactions, and not from transactions that have just been executed or + dry-run. + """ + transactionBlock: TransactionBlock """ The Move module containing some function that when called by a programmable transaction block (PTB) emitted this event. @@ -2340,6 +2359,10 @@ type MovePackage implements IObject & IOwner { """ typeOrigins: [TypeOrigin!] """ + BCS representation of the package itself, as a MovePackage. + """ + packageBcs: Base64 + """ BCS representation of the package's modules. Modules appear as a sequence of pairs (module name, followed by module bytes), in alphabetic order by module name. """ @@ -2877,11 +2900,6 @@ enum ObjectKind { The object is fetched from the index. """ INDEXED - """ - The object is deleted or wrapped and only partial information can be loaded from the - indexer. - """ - WRAPPED_OR_DELETED } """ @@ -3057,11 +3075,13 @@ type PageInfo { """ If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -the dynamic field, or the intermediate Field object itself). Also note that if the owner -is a parent, then it's guaranteed to be an object. +the dynamic field, or the intermediate Field object itself), and it is owned by another object. + +Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +object could be wrapped and therefore not directly accessible. """ type Parent { - parent: Object + parent: Owner } """ @@ -3292,6 +3312,7 @@ type Query { `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + epochs(first: Int, after: String, last: Int, before: String): EpochConnection! """ The checkpoints that exist in the network. """ @@ -4189,7 +4210,7 @@ type TransactionBlock { """ expiration: Epoch """ - Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. """ bcs: Base64 } @@ -4324,27 +4345,10 @@ input TransactionBlockFilter { """ affectedObject: SuiAddress """ - Limit to transactions that were sent by the given address. NOTE: this input filter has been - deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - Both filters restrict transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - signAddress: SuiAddress - """ Limit to transactions that were sent by the given address. """ sentAddress: SuiAddress """ - Limit to transactions that sent an object to the given address. NOTE: this input filter has - been deprecated in favor of `affectedAddress` which offers an easier to understand - behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `affectedAddress` is introduced, whichever is later. - """ - recvAddress: SuiAddress - """ Limit to transactions that accepted the given object as an input. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to under behavior. diff --git a/crates/sui-graphql-rpc/tests/e2e_tests.rs b/crates/sui-graphql-rpc/tests/e2e_tests.rs index 391e0a30c5dc3..b51f94e40ccea 100644 --- a/crates/sui-graphql-rpc/tests/e2e_tests.rs +++ b/crates/sui-graphql-rpc/tests/e2e_tests.rs @@ -277,8 +277,6 @@ async fn test_graphql_client_variables() { #[tokio::test] async fn test_transaction_execution() { - telemetry_subscribers::init_for_testing(); - let cluster = start_cluster(ServiceConfig::test_defaults()).await; let addresses = cluster diff --git a/crates/sui-graphql-rpc/tests/move_registry_e2e.rs b/crates/sui-graphql-rpc/tests/move_registry_e2e.rs index 79455ea7ff635..63a83429ee8d3 100644 --- a/crates/sui-graphql-rpc/tests/move_registry_e2e.rs +++ b/crates/sui-graphql-rpc/tests/move_registry_e2e.rs @@ -215,16 +215,12 @@ fn test_results( ); assert_eq!( - query_result["data"]["v1_type"]["layout"]["struct"]["type"] - .as_str() - .unwrap(), + query_result["data"]["v1_type"]["repr"].as_str().unwrap(), format!("{}{}", v1, DEMO_TYPE) ); assert_eq!( - query_result["data"]["v2_type"]["layout"]["struct"]["type"] - .as_str() - .unwrap(), + query_result["data"]["v2_type"]["repr"].as_str().unwrap(), format!("{}{}", v2, DEMO_TYPE_V2) ); @@ -234,6 +230,13 @@ fn test_results( .unwrap(), format!("{}{}", v3, DEMO_TYPE_V3) ); + + assert_eq!( + query_result["data"]["v3_type"]["layout"]["struct"]["type"] + .as_str() + .unwrap(), + query_result["data"]["v3_type"]["repr"].as_str().unwrap() + ); } async fn init_move_registry_gql( @@ -519,5 +522,5 @@ fn name_query(name: &str) -> String { } fn type_query(named_type: &str) -> String { - format!(r#"typeByName(name: "{}") {{ layout }}"#, named_type) + format!(r#"typeByName(name: "{}") {{ layout, repr }}"#, named_type) } diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema.graphql.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema.graphql.snap index e64bc7cc1eee2..70071a6173061 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema.graphql.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema.graphql.snap @@ -167,26 +167,15 @@ type AddressOwner { The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { - """ - Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - `SENT` which behaves identically but is named more clearly. Both filters restrict - transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - SIGN """ Transactions this address has sent. """ SENT """ - Transactions that sent objects to this address. NOTE: this input filter has been deprecated - in favor of `AFFECTED`, which offers an easier to understand behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `AFFECTED` is introduced, whichever is later. + Transactions that this address was involved in, either as the sender, sponsor, or as the + owner of some object that was created, modified or transfered. """ - RECV + AFFECTED } """ @@ -1204,7 +1193,42 @@ type Epoch { transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } +type EpochConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EpochEdge!]! + """ + A list of nodes. + """ + nodes: [Epoch!]! +} + +""" +An edge in a connection. +""" +type EpochEdge { + """ + The item at the end of the edge + """ + node: Epoch! + """ + A cursor for use in pagination + """ + cursor: String! +} + type Event { + """ + The transaction block that emitted this event. This information is only available for + events from indexed transactions, and not from transactions that have just been executed or + dry-run. + """ + transactionBlock: TransactionBlock """ The Move module containing some function that when called by a programmable transaction block (PTB) emitted this event. @@ -2339,6 +2363,10 @@ type MovePackage implements IObject & IOwner { """ typeOrigins: [TypeOrigin!] """ + BCS representation of the package itself, as a MovePackage. + """ + packageBcs: Base64 + """ BCS representation of the package's modules. Modules appear as a sequence of pairs (module name, followed by module bytes), in alphabetic order by module name. """ @@ -2876,11 +2904,6 @@ enum ObjectKind { The object is fetched from the index. """ INDEXED - """ - The object is deleted or wrapped and only partial information can be loaded from the - indexer. - """ - WRAPPED_OR_DELETED } """ @@ -3056,11 +3079,13 @@ type PageInfo { """ If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -the dynamic field, or the intermediate Field object itself). Also note that if the owner -is a parent, then it's guaranteed to be an object. +the dynamic field, or the intermediate Field object itself), and it is owned by another object. + +Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +object could be wrapped and therefore not directly accessible. """ type Parent { - parent: Object + parent: Owner } """ @@ -3291,6 +3316,7 @@ type Query { `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + epochs(first: Int, after: String, last: Int, before: String): EpochConnection! """ The checkpoints that exist in the network. """ @@ -4188,7 +4214,7 @@ type TransactionBlock { """ expiration: Epoch """ - Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. """ bcs: Base64 } @@ -4311,27 +4337,15 @@ input TransactionBlockFilter { """ beforeCheckpoint: UInt53 """ - Limit to transactions that were sent by the given address. NOTE: this input filter has been - deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - Both filters restrict transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). + Limit to transactions that interacted with the given address. The address could be a + sender, sponsor, or recipient of the transaction. """ - signAddress: SuiAddress + affectedAddress: SuiAddress """ Limit to transactions that were sent by the given address. """ sentAddress: SuiAddress """ - Limit to transactions that sent an object to the given address. NOTE: this input filter has - been deprecated in favor of `affectedAddress` which offers an easier to understand - behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `affectedAddress` is introduced, whichever is later. - """ - recvAddress: SuiAddress - """ Limit to transactions that accepted the given object as an input. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to under behavior. diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__staging.graphql.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__staging.graphql.snap index 3bd0889eb8cb8..498feb7c2fafb 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__staging.graphql.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__staging.graphql.snap @@ -167,27 +167,11 @@ type AddressOwner { The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { - """ - Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - `SENT` which behaves identically but is named more clearly. Both filters restrict - transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - SIGN """ Transactions this address has sent. """ SENT """ - Transactions that sent objects to this address. NOTE: this input filter has been deprecated - in favor of `AFFECTED`, which offers an easier to understand behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `AFFECTED` is introduced, whichever is later. - """ - RECV - """ Transactions that this address was involved in, either as the sender, sponsor, or as the owner of some object that was created, modified or transfered. """ @@ -1209,7 +1193,42 @@ type Epoch { transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } +type EpochConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EpochEdge!]! + """ + A list of nodes. + """ + nodes: [Epoch!]! +} + +""" +An edge in a connection. +""" +type EpochEdge { + """ + The item at the end of the edge + """ + node: Epoch! + """ + A cursor for use in pagination + """ + cursor: String! +} + type Event { + """ + The transaction block that emitted this event. This information is only available for + events from indexed transactions, and not from transactions that have just been executed or + dry-run. + """ + transactionBlock: TransactionBlock """ The Move module containing some function that when called by a programmable transaction block (PTB) emitted this event. @@ -2344,6 +2363,10 @@ type MovePackage implements IObject & IOwner { """ typeOrigins: [TypeOrigin!] """ + BCS representation of the package itself, as a MovePackage. + """ + packageBcs: Base64 + """ BCS representation of the package's modules. Modules appear as a sequence of pairs (module name, followed by module bytes), in alphabetic order by module name. """ @@ -2881,11 +2904,6 @@ enum ObjectKind { The object is fetched from the index. """ INDEXED - """ - The object is deleted or wrapped and only partial information can be loaded from the - indexer. - """ - WRAPPED_OR_DELETED } """ @@ -3061,11 +3079,13 @@ type PageInfo { """ If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -the dynamic field, or the intermediate Field object itself). Also note that if the owner -is a parent, then it's guaranteed to be an object. +the dynamic field, or the intermediate Field object itself), and it is owned by another object. + +Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +object could be wrapped and therefore not directly accessible. """ type Parent { - parent: Object + parent: Owner } """ @@ -3296,6 +3316,7 @@ type Query { `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + epochs(first: Int, after: String, last: Int, before: String): EpochConnection! """ The checkpoints that exist in the network. """ @@ -4193,7 +4214,7 @@ type TransactionBlock { """ expiration: Epoch """ - Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. """ bcs: Base64 } @@ -4328,27 +4349,10 @@ input TransactionBlockFilter { """ affectedObject: SuiAddress """ - Limit to transactions that were sent by the given address. NOTE: this input filter has been - deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - Both filters restrict transactions by their sender, only, not signers in general. - - This filter will be removed with 1.36.0 (2024-10-14). - """ - signAddress: SuiAddress - """ Limit to transactions that were sent by the given address. """ sentAddress: SuiAddress """ - Limit to transactions that sent an object to the given address. NOTE: this input filter has - been deprecated in favor of `affectedAddress` which offers an easier to understand - behavior. - - This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - `affectedAddress` is introduced, whichever is later. - """ - recvAddress: SuiAddress - """ Limit to transactions that accepted the given object as an input. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to under behavior. diff --git a/crates/sui-indexer/Cargo.toml b/crates/sui-indexer/Cargo.toml index 3442cf46fa20d..fa4490741b163 100644 --- a/crates/sui-indexer/Cargo.toml +++ b/crates/sui-indexer/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] anyhow.workspace = true +rand = "0.8.5" async-trait.workspace = true axum.workspace = true backoff.workspace = true diff --git a/crates/sui-indexer/migrations/pg/2024-09-12-213234_watermarks/up.sql b/crates/sui-indexer/migrations/pg/2024-09-12-213234_watermarks/up.sql index 1eaeadb4866f7..e8499c692120c 100644 --- a/crates/sui-indexer/migrations/pg/2024-09-12-213234_watermarks/up.sql +++ b/crates/sui-indexer/migrations/pg/2024-09-12-213234_watermarks/up.sql @@ -4,16 +4,16 @@ CREATE TABLE watermarks entity TEXT NOT NULL, -- Inclusive upper epoch bound for this entity's data. Committer updates this field. Pruner uses -- this to determine if pruning is necessary based on the retention policy. - epoch_hi BIGINT NOT NULL, + epoch_hi_inclusive BIGINT NOT NULL, -- Inclusive lower epoch bound for this entity's data. Pruner updates this field when the epoch range exceeds the retention policy. epoch_lo BIGINT NOT NULL, -- Inclusive upper checkpoint bound for this entity's data. Committer updates this field. All -- data of this entity in the checkpoint must be persisted before advancing this watermark. The -- committer refers to this on disaster recovery to resume writing. - checkpoint_hi BIGINT NOT NULL, + checkpoint_hi_inclusive BIGINT NOT NULL, -- Inclusive upper transaction sequence number bound for this entity's data. Committer updates -- this field. - tx_hi BIGINT NOT NULL, + tx_hi_inclusive BIGINT NOT NULL, -- Inclusive low watermark that the pruner advances. Corresponds to the epoch id, checkpoint -- sequence number, or tx sequence number depending on the entity. Data before this watermark is -- considered pruned by a reader. The underlying data may still exist in the db instance. @@ -22,9 +22,8 @@ CREATE TABLE watermarks -- be dropped. The pruner uses this column to determine whether to prune or wait long enough -- that all in-flight reads complete or timeout before it acts on an updated watermark. timestamp_ms BIGINT NOT NULL, - -- Column used by the pruner to track its true progress. Data at and below this watermark has - -- been truly pruned from the db, and should no longer exist. When recovering from a crash, the - -- pruner will consult this column to determine where to continue. - pruned_lo BIGINT, + -- Column used by the pruner to track its true progress. Data at and below this watermark can + -- be immediately pruned. + pruner_lo BIGINT, PRIMARY KEY (entity) ); diff --git a/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/down.sql b/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/down.sql new file mode 100644 index 0000000000000..82659b80658c0 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/down.sql @@ -0,0 +1,7 @@ +-- Drop the new partial indices +DROP INDEX IF EXISTS objects_history_owner_partial; +DROP INDEX IF EXISTS objects_history_coin_owner_partial; +DROP INDEX IF EXISTS objects_history_coin_only_partial; +DROP INDEX IF EXISTS objects_history_type_partial; +DROP INDEX IF EXISTS objects_history_package_module_name_full_type_partial; +DROP INDEX IF EXISTS objects_history_owner_package_module_name_full_type_partial; diff --git a/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/up.sql b/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/up.sql new file mode 100644 index 0000000000000..800f77b3f540b --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-10-08-025030_partial_index_instead/up.sql @@ -0,0 +1,18 @@ +-- Create new partial indices with object_status = 0 condition +CREATE INDEX IF NOT EXISTS objects_history_owner_partial ON objects_history (checkpoint_sequence_number, owner_type, owner_id) +WHERE owner_type BETWEEN 1 AND 2 AND owner_id IS NOT NULL AND object_status = 0; + +CREATE INDEX IF NOT EXISTS objects_history_coin_owner_partial ON objects_history (checkpoint_sequence_number, owner_id, coin_type, object_id) +WHERE coin_type IS NOT NULL AND owner_type = 1 AND object_status = 0; + +CREATE INDEX IF NOT EXISTS objects_history_coin_only_partial ON objects_history (checkpoint_sequence_number, coin_type, object_id) +WHERE coin_type IS NOT NULL AND object_status = 0; + +CREATE INDEX IF NOT EXISTS objects_history_type_partial ON objects_history (checkpoint_sequence_number, object_type) +WHERE object_status = 0; + +CREATE INDEX IF NOT EXISTS objects_history_package_module_name_full_type_partial ON objects_history (checkpoint_sequence_number, object_type_package, object_type_module, object_type_name, object_type) +WHERE object_status = 0; + +CREATE INDEX IF NOT EXISTS objects_history_owner_package_module_name_full_type_partial ON objects_history (checkpoint_sequence_number, owner_id, object_type_package, object_type_module, object_type_name, object_type) +WHERE object_status = 0; diff --git a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/ingestion_backfill_task.rs b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/ingestion_backfill_task.rs index c496a91471a45..2702f755c0842 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/ingestion_backfill_task.rs +++ b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/ingestion_backfill_task.rs @@ -57,6 +57,7 @@ pub struct Adapter { #[async_trait::async_trait] impl Worker for Adapter { + type Result = (); async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> anyhow::Result<()> { let processed = T::process_checkpoint(checkpoint); self.ready_checkpoints diff --git a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/mod.rs b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/mod.rs index 503463e237e4d..17bbc29d7dc5c 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/mod.rs +++ b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/mod.rs @@ -1,8 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -pub mod ingestion_backfill_task; -pub mod raw_checkpoints; +pub(crate) mod ingestion_backfill_task; +pub(crate) mod raw_checkpoints; +pub(crate) mod tx_affected_objects; use crate::database::ConnectionPool; use sui_types::full_checkpoint_content::CheckpointData; diff --git a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/raw_checkpoints.rs b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/raw_checkpoints.rs index e18219f6cf079..aec4f0263ee80 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/raw_checkpoints.rs +++ b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/raw_checkpoints.rs @@ -8,7 +8,7 @@ use crate::schema::raw_checkpoints::dsl::raw_checkpoints; use diesel_async::RunQueryDsl; use sui_types::full_checkpoint_content::CheckpointData; -pub struct RawCheckpointsBackFill {} +pub struct RawCheckpointsBackFill; #[async_trait::async_trait] impl IngestionBackfillTrait for RawCheckpointsBackFill { diff --git a/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/tx_affected_objects.rs b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/tx_affected_objects.rs new file mode 100644 index 0000000000000..4e6f6efa6a897 --- /dev/null +++ b/crates/sui-indexer/src/backfill/backfill_instances/ingestion_backfills/tx_affected_objects.rs @@ -0,0 +1,48 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::backfill::backfill_instances::ingestion_backfills::IngestionBackfillTrait; +use crate::database::ConnectionPool; +use crate::models::tx_indices::StoredTxAffectedObjects; +use crate::schema::tx_affected_objects; +use diesel_async::RunQueryDsl; +use sui_types::effects::TransactionEffectsAPI; +use sui_types::full_checkpoint_content::CheckpointData; + +pub struct TxAffectedObjectsBackfill; + +#[async_trait::async_trait] +impl IngestionBackfillTrait for TxAffectedObjectsBackfill { + type ProcessedType = StoredTxAffectedObjects; + + fn process_checkpoint(checkpoint: &CheckpointData) -> Vec { + let first_tx = checkpoint.checkpoint_summary.network_total_transactions as usize + - checkpoint.transactions.len(); + + checkpoint + .transactions + .iter() + .enumerate() + .flat_map(|(i, tx)| { + tx.effects + .object_changes() + .into_iter() + .map(move |change| StoredTxAffectedObjects { + tx_sequence_number: (first_tx + i) as i64, + affected: change.id.to_vec(), + sender: tx.transaction.sender_address().to_vec(), + }) + }) + .collect() + } + + async fn commit_chunk(pool: ConnectionPool, processed_data: Vec) { + let mut conn = pool.get().await.unwrap(); + diesel::insert_into(tx_affected_objects::table) + .values(processed_data) + .on_conflict_do_nothing() + .execute(&mut conn) + .await + .unwrap(); + } +} diff --git a/crates/sui-indexer/src/backfill/backfill_instances/mod.rs b/crates/sui-indexer/src/backfill/backfill_instances/mod.rs index 3fa673875793b..27c96dd6c9234 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/mod.rs +++ b/crates/sui-indexer/src/backfill/backfill_instances/mod.rs @@ -3,6 +3,7 @@ use crate::backfill::backfill_instances::ingestion_backfills::ingestion_backfill_task::IngestionBackfillTask; use crate::backfill::backfill_instances::ingestion_backfills::raw_checkpoints::RawCheckpointsBackFill; +use crate::backfill::backfill_instances::ingestion_backfills::tx_affected_objects::TxAffectedObjectsBackfill; use crate::backfill::backfill_task::BackfillTask; use crate::backfill::{BackfillTaskKind, IngestionBackfillKind}; use std::sync::Arc; @@ -11,7 +12,6 @@ use sui_types::messages_checkpoint::CheckpointSequenceNumber; mod ingestion_backfills; mod sql_backfill; mod system_state_summary_json; -mod tx_affected_objects; pub async fn get_backfill_task( kind: BackfillTaskKind, @@ -21,9 +21,6 @@ pub async fn get_backfill_task( BackfillTaskKind::SystemStateSummaryJson => { Arc::new(system_state_summary_json::SystemStateSummaryJsonBackfill) } - BackfillTaskKind::TxAffectedObjects => { - Arc::new(tx_affected_objects::TxAffectedObjectsBackfill) - } BackfillTaskKind::Sql { sql, key_column } => { Arc::new(sql_backfill::SqlBackFill::new(sql, key_column)) } @@ -38,6 +35,13 @@ pub async fn get_backfill_task( ) .await, ), + IngestionBackfillKind::TxAffectedObjects => Arc::new( + IngestionBackfillTask::::new( + remote_store_url, + range_start as CheckpointSequenceNumber, + ) + .await, + ), }, } } diff --git a/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/event_sender.sh b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/event_sender.sh index a4d003991e6d7..f214c55f7d7eb 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/event_sender.sh +++ b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/event_sender.sh @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 INDEXER=${INDEXER:-"sui-indexer"} -DB=${DB:-"postgres://postgres@localhost:5432/postgres"} +DB=${DB:-"postgres://postgres:postgrespw@localhost:5432/postgres"} "$INDEXER" --database-url "$DB" run-back-fill "$1" "$2" sql "UPDATE events SET sender = CASE WHEN cardinality(senders) > 0 THEN senders[1] ELSE NULL END" checkpoint_sequence_number diff --git a/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/full_objects_history.sh b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/full_objects_history.sh index c42e79f38d7ad..2492b477ed592 100644 --- a/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/full_objects_history.sh +++ b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/full_objects_history.sh @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 INDEXER=${INDEXER:-"sui-indexer"} -DB=${DB:-"postgres://postgres@localhost:5432/postgres"} +DB=${DB:-"postgres://postgres:postgrespw@localhost:5432/postgres"} "$INDEXER" --database-url "$DB" run-back-fill "$1" "$2" sql "INSERT INTO full_objects_history (object_id, object_version, serialized_object) SELECT object_id, object_version, serialized_object FROM objects_history" checkpoint_sequence_number diff --git a/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/tx_affected_addresses.sh b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/tx_affected_addresses.sh new file mode 100644 index 0000000000000..df1212e18bbc6 --- /dev/null +++ b/crates/sui-indexer/src/backfill/backfill_instances/sql_backfills/tx_affected_addresses.sh @@ -0,0 +1,7 @@ +# Copyright (c) Mysten Labs, Inc. +# SPDX-License-Identifier: Apache-2.0 + +INDEXER=${INDEXER:-"sui-indexer"} +DB=${DB:-"postgres://postgres:postgrespw@localhost:5432/postgres"} +"$INDEXER" --database-url "$DB" run-back-fill "$1" "$2" sql "INSERT INTO tx_affected_addresses SELECT tx_sequence_number, sender AS affected, sender FROM tx_senders" tx_sequence_number +"$INDEXER" --database-url "$DB" run-back-fill "$1" "$2" sql "INSERT INTO tx_affected_addresses SELECT tx_sequence_number, recipient AS affected, sender FROM tx_recipients" tx_sequence_number diff --git a/crates/sui-indexer/src/backfill/backfill_instances/tx_affected_objects.rs b/crates/sui-indexer/src/backfill/backfill_instances/tx_affected_objects.rs deleted file mode 100644 index 2403d5352e405..0000000000000 --- a/crates/sui-indexer/src/backfill/backfill_instances/tx_affected_objects.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::RangeInclusive; - -use async_trait::async_trait; -use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl}; -use diesel_async::RunQueryDsl; -use sui_types::effects::{TransactionEffects, TransactionEffectsAPI}; - -use crate::{ - backfill::backfill_task::BackfillTask, - database::ConnectionPool, - models::tx_indices::StoredTxAffectedObjects, - schema::{transactions, tx_affected_objects, tx_senders}, -}; - -pub struct TxAffectedObjectsBackfill; - -#[async_trait] -impl BackfillTask for TxAffectedObjectsBackfill { - async fn backfill_range(&self, pool: ConnectionPool, range: &RangeInclusive) { - use transactions::dsl as tx; - use tx_senders::dsl as ts; - - let mut conn = pool.get().await.unwrap(); - - let join = tx_senders::table.on(tx::tx_sequence_number.eq(ts::tx_sequence_number)); - - let results: Vec<(i64, Vec, Vec)> = transactions::table - .inner_join(join) - .select((tx::tx_sequence_number, ts::sender, tx::raw_effects)) - .filter(tx::tx_sequence_number.between(*range.start() as i64, *range.end() as i64)) - .load(&mut conn) - .await - .unwrap(); - - let effects: Vec<(i64, Vec, TransactionEffects)> = results - .into_iter() - .map(|(tx_sequence_number, sender, bytes)| { - (tx_sequence_number, sender, bcs::from_bytes(&bytes).unwrap()) - }) - .collect(); - - let affected_objects: Vec = effects - .into_iter() - .flat_map(|(tx_sequence_number, sender, effects)| { - effects - .object_changes() - .into_iter() - .map(move |change| StoredTxAffectedObjects { - tx_sequence_number, - affected: change.id.to_vec(), - sender: sender.clone(), - }) - }) - .collect(); - - for chunk in affected_objects.chunks(1000) { - diesel::insert_into(tx_affected_objects::table) - .values(chunk) - .on_conflict_do_nothing() - .execute(&mut conn) - .await - .unwrap(); - } - } -} diff --git a/crates/sui-indexer/src/backfill/mod.rs b/crates/sui-indexer/src/backfill/mod.rs index 92a8e3bfb1968..453d11baeeed2 100644 --- a/crates/sui-indexer/src/backfill/mod.rs +++ b/crates/sui-indexer/src/backfill/mod.rs @@ -10,7 +10,6 @@ pub mod backfill_task; #[derive(Subcommand, Clone, Debug)] pub enum BackfillTaskKind { SystemStateSummaryJson, - TxAffectedObjects, /// \sql is the SQL string to run, appended with the range between the start and end, /// as well as conflict resolution (see sql_backfill.rs). /// \key_column is the primary key column to use for the range. @@ -31,4 +30,5 @@ pub enum BackfillTaskKind { #[derive(ValueEnum, Clone, Debug)] pub enum IngestionBackfillKind { RawCheckpoints, + TxAffectedObjects, } diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index 96c15af2ffe74..4f722ba8d0b3b 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -87,6 +87,7 @@ pub struct CheckpointHandler { #[async_trait] impl Worker for CheckpointHandler { + type Result = (); async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> anyhow::Result<()> { let time_now_ms = chrono::Utc::now().timestamp_millis(); let cp_download_lag = time_now_ms - checkpoint.checkpoint_summary.timestamp_ms as i64; diff --git a/crates/sui-indexer/src/handlers/committer.rs b/crates/sui-indexer/src/handlers/committer.rs index d11a5cfda3bc9..f67f2fad6f007 100644 --- a/crates/sui-indexer/src/handlers/committer.rs +++ b/crates/sui-indexer/src/handlers/committer.rs @@ -73,7 +73,7 @@ where })?; } } - if !batch.is_empty() && unprocessed.is_empty() { + if !batch.is_empty() { commit_checkpoints(&state, batch, None, &metrics).await; batch = vec![]; } @@ -174,7 +174,7 @@ async fn commit_checkpoints( state.persist_objects(object_changes_batch.clone()), state.persist_object_history(object_history_changes_batch.clone()), state.persist_full_objects_history(object_history_changes_batch.clone()), - state.persist_object_versions(object_versions_batch.clone()), + state.persist_objects_version(object_versions_batch.clone()), state.persist_raw_checkpoints(raw_checkpoints_batch), ]; if let Some(epoch_data) = epoch.clone() { @@ -219,17 +219,6 @@ async fn commit_checkpoints( }) .expect("Persisting data into DB should not fail."); - state - .update_watermarks_upper_bound::(committer_watermark) - .await - .tap_err(|e| { - error!( - "Failed to update watermark upper bound with error: {}", - e.to_string() - ); - }) - .expect("Updating watermark upper bound in DB should not fail."); - if is_epoch_end { // The epoch has advanced so we update the configs for the new protocol version, if it has changed. let chain_id = state @@ -242,6 +231,17 @@ async fn commit_checkpoints( .await; } + state + .update_watermarks_upper_bound::(committer_watermark) + .await + .tap_err(|e| { + error!( + "Failed to update watermark upper bound with error: {}", + e.to_string() + ); + }) + .expect("Updating watermark upper bound in DB should not fail."); + let elapsed = guard.stop_and_record(); info!( diff --git a/crates/sui-indexer/src/handlers/mod.rs b/crates/sui-indexer/src/handlers/mod.rs index 3f9d0357c233a..75b64394f1ba3 100644 --- a/crates/sui-indexer/src/handlers/mod.rs +++ b/crates/sui-indexer/src/handlers/mod.rs @@ -195,7 +195,7 @@ impl From<&CheckpointData> for CommitterWatermark { } } -/// Enum representing tables that a committer updates. +/// Enum representing tables that the committer handler writes to. #[derive( Debug, Eq, @@ -253,7 +253,7 @@ pub enum CommitterTables { PrunerCpWatermark, } -/// Enum representing tables that the objects snapshot processor updates. +/// Enum representing tables that the objects snapshot handler writes to. #[derive( Debug, Eq, diff --git a/crates/sui-indexer/src/handlers/objects_snapshot_handler.rs b/crates/sui-indexer/src/handlers/objects_snapshot_handler.rs index 5f8e285539122..960f057b54ddd 100644 --- a/crates/sui-indexer/src/handlers/objects_snapshot_handler.rs +++ b/crates/sui-indexer/src/handlers/objects_snapshot_handler.rs @@ -34,6 +34,7 @@ pub struct CheckpointObjectChanges { #[async_trait] impl Worker for ObjectsSnapshotHandler { + type Result = (); async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> anyhow::Result<()> { let transformed_data = CheckpointHandler::index_objects(checkpoint, &self.metrics).await?; self.sender diff --git a/crates/sui-indexer/src/indexer_reader.rs b/crates/sui-indexer/src/indexer_reader.rs index cd816ef6cdd1d..a30cde7bb88df 100644 --- a/crates/sui-indexer/src/indexer_reader.rs +++ b/crates/sui-indexer/src/indexer_reader.rs @@ -737,8 +737,8 @@ impl IndexerReader { Some(TransactionFilter::FromAddress(from_address)) => { let from_address = Hex::encode(from_address.to_vec()); ( - "tx_senders".to_owned(), - format!("sender = '\\x{from_address}'::bytea"), + "tx_affected_addresses".to_owned(), + format!("sender = '\\x{from_address}'::bytea AND affected = '\\x{from_address}'::bytea"), ) } Some(TransactionFilter::FromAndToAddress { from, to }) => { @@ -997,11 +997,10 @@ impl IndexerReader { format!( "( \ SELECT * - FROM tx_senders s + FROM event_senders s JOIN events e - ON e.tx_sequence_number = s.tx_sequence_number - AND s.sender = '\\x{}'::bytea - WHERE {} \ + USING (tx_sequence_number, event_sequence_number) + WHERE s.sender = '\\x{}'::bytea AND {} \ ORDER BY {} \ LIMIT {} )", @@ -1042,7 +1041,7 @@ impl IndexerReader { // Processed above unreachable!() } - EventFilter::TimeRange { .. } => { + EventFilter::TimeRange { .. } | EventFilter::Any(_) => { return Err(IndexerError::NotSupportedError( "This type of EventFilter is not supported.".to_owned(), )); diff --git a/crates/sui-indexer/src/metrics.rs b/crates/sui-indexer/src/metrics.rs index e6e3411ecdbbb..0b1f8c1e5bed5 100644 --- a/crates/sui-indexer/src/metrics.rs +++ b/crates/sui-indexer/src/metrics.rs @@ -110,10 +110,12 @@ pub struct IndexerMetrics { pub checkpoint_db_commit_latency_transactions_chunks_transformation: Histogram, pub checkpoint_db_commit_latency_objects: Histogram, pub checkpoint_db_commit_latency_objects_snapshot: Histogram, + pub checkpoint_db_commit_latency_objects_version: Histogram, pub checkpoint_db_commit_latency_objects_history: Histogram, pub checkpoint_db_commit_latency_full_objects_history: Histogram, pub checkpoint_db_commit_latency_objects_chunks: Histogram, pub checkpoint_db_commit_latency_objects_snapshot_chunks: Histogram, + pub checkpoint_db_commit_latency_objects_version_chunks: Histogram, pub checkpoint_db_commit_latency_objects_history_chunks: Histogram, pub checkpoint_db_commit_latency_full_objects_history_chunks: Histogram, pub checkpoint_db_commit_latency_events: Histogram, @@ -435,6 +437,12 @@ impl IndexerMetrics { registry, ) .unwrap(), + checkpoint_db_commit_latency_objects_version: register_histogram_with_registry!( + "checkpoint_db_commit_latency_objects_version", + "Time spent committing objects version", + DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), + registry, + ).unwrap(), checkpoint_db_commit_latency_objects_history: register_histogram_with_registry!( "checkpoint_db_commit_latency_objects_history", "Time spent committing objects history", @@ -461,6 +469,12 @@ impl IndexerMetrics { registry, ) .unwrap(), + checkpoint_db_commit_latency_objects_version_chunks: register_histogram_with_registry!( + "checkpoint_db_commit_latency_objects_version_chunks", + "Time spent committing objects version chunks", + DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), + registry, + ).unwrap(), checkpoint_db_commit_latency_objects_history_chunks: register_histogram_with_registry!( "checkpoint_db_commit_latency_objects_history_chunks", "Time spent committing objects history chunks", diff --git a/crates/sui-indexer/src/models/tx_indices.rs b/crates/sui-indexer/src/models/tx_indices.rs index d601153ecee51..a00b715eedf98 100644 --- a/crates/sui-indexer/src/models/tx_indices.rs +++ b/crates/sui-indexer/src/models/tx_indices.rs @@ -4,7 +4,7 @@ use crate::{ schema::{ tx_affected_addresses, tx_affected_objects, tx_calls_fun, tx_calls_mod, tx_calls_pkg, - tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, tx_recipients, tx_senders, + tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, }, types::TxIndex, }; @@ -39,21 +39,6 @@ pub struct StoredTxAffectedObjects { pub sender: Vec, } -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_senders)] -pub struct StoredTxSenders { - pub tx_sequence_number: i64, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_recipients)] -pub struct StoredTxRecipients { - pub tx_sequence_number: i64, - pub recipient: Vec, - pub sender: Vec, -} - #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_input_objects)] pub struct StoredTxInputObject { @@ -118,8 +103,6 @@ impl TxIndex { ) -> ( Vec, Vec, - Vec, - Vec, Vec, Vec, Vec, @@ -153,21 +136,6 @@ impl TxIndex { }) .collect(); - let tx_sender = StoredTxSenders { - tx_sequence_number, - sender: self.sender.to_vec(), - }; - - let tx_recipients = self - .recipients - .iter() - .map(|s| StoredTxRecipients { - tx_sequence_number, - recipient: s.to_vec(), - sender: self.sender.to_vec(), - }) - .collect(); - let tx_input_objects = self .input_objects .iter() @@ -245,8 +213,6 @@ impl TxIndex { ( tx_affected_addresses, tx_affected_objects, - vec![tx_sender], - tx_recipients, tx_input_objects, tx_changed_objects, tx_pkgs, diff --git a/crates/sui-indexer/src/models/watermarks.rs b/crates/sui-indexer/src/models/watermarks.rs index b4cb8032c7e0d..e3c2395fec636 100644 --- a/crates/sui-indexer/src/models/watermarks.rs +++ b/crates/sui-indexer/src/models/watermarks.rs @@ -15,16 +15,16 @@ pub struct StoredWatermark { pub entity: String, /// Inclusive upper epoch bound for this entity's data. Committer updates this field. Pruner uses /// this to determine if pruning is necessary based on the retention policy. - pub epoch_hi: i64, + pub epoch_hi_inclusive: i64, /// Inclusive lower epoch bound for this entity's data. Pruner updates this field when the epoch range exceeds the retention policy. pub epoch_lo: i64, /// Inclusive upper checkpoint bound for this entity's data. Committer updates this field. All /// data of this entity in the checkpoint must be persisted before advancing this watermark. The /// committer refers to this on disaster recovery to resume writing. - pub checkpoint_hi: i64, + pub checkpoint_hi_inclusive: i64, /// Inclusive upper transaction sequence number bound for this entity's data. Committer updates /// this field. - pub tx_hi: i64, + pub tx_hi_inclusive: i64, /// Inclusive low watermark that the pruner advances. Corresponds to the epoch id, checkpoint /// sequence number, or tx sequence number depending on the entity. Data before this watermark is /// considered pruned by a reader. The underlying data may still exist in the db instance. @@ -33,19 +33,18 @@ pub struct StoredWatermark { /// be dropped. The pruner uses this column to determine whether to prune or wait long enough /// that all in-flight reads complete or timeout before it acts on an updated watermark. pub timestamp_ms: i64, - /// Column used by the pruner to track its true progress. Data at and below this watermark has - /// been truly pruned from the db, and should no longer exist. When recovering from a crash, the - /// pruner will consult this column to determine where to continue. - pub pruned_lo: Option, + /// Column used by the pruner to track its true progress. Data at and below this watermark can + /// be immediately pruned. + pub pruner_lo: Option, } impl StoredWatermark { pub fn from_upper_bound_update(entity: &str, watermark: CommitterWatermark) -> Self { StoredWatermark { entity: entity.to_string(), - epoch_hi: watermark.epoch as i64, - checkpoint_hi: watermark.cp as i64, - tx_hi: watermark.tx as i64, + epoch_hi_inclusive: watermark.epoch as i64, + checkpoint_hi_inclusive: watermark.cp as i64, + tx_hi_inclusive: watermark.tx as i64, ..StoredWatermark::default() } } diff --git a/crates/sui-indexer/src/schema.rs b/crates/sui-indexer/src/schema.rs index 31c67932cd8cd..f56872fc80e90 100644 --- a/crates/sui-indexer/src/schema.rs +++ b/crates/sui-indexer/src/schema.rs @@ -371,13 +371,13 @@ diesel::table! { diesel::table! { watermarks (entity) { entity -> Text, - epoch_hi -> Int8, + epoch_hi_inclusive -> Int8, epoch_lo -> Int8, - checkpoint_hi -> Int8, - tx_hi -> Int8, + checkpoint_hi_inclusive -> Int8, + tx_hi_inclusive -> Int8, reader_lo -> Int8, timestamp_ms -> Int8, - pruned_lo -> Nullable, + pruner_lo -> Nullable, } } diff --git a/crates/sui-indexer/src/store/indexer_store.rs b/crates/sui-indexer/src/store/indexer_store.rs index d90a3d9d558ee..f623a72f70fe2 100644 --- a/crates/sui-indexer/src/store/indexer_store.rs +++ b/crates/sui-indexer/src/store/indexer_store.rs @@ -56,7 +56,7 @@ pub trait IndexerStore: Clone + Sync + Send + 'static { object_changes: Vec, ) -> Result<(), IndexerError>; - async fn persist_object_versions( + async fn persist_objects_version( &self, object_versions: Vec, ) -> Result<(), IndexerError>; diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index bc2b0131ff1b0..0101e4989697f 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -53,7 +53,7 @@ use crate::schema::{ objects_snapshot, objects_version, packages, protocol_configs, pruner_cp_watermark, raw_checkpoints, transactions, tx_affected_addresses, tx_affected_objects, tx_calls_fun, tx_calls_mod, tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, - tx_recipients, tx_senders, watermarks, + watermarks, }; use crate::store::transaction_with_retry; use crate::types::{EventIndex, IndexedDeletedObject, IndexedObject}; @@ -298,7 +298,7 @@ impl PgIndexerStore { let mut connection = self.pool.get().await?; watermarks::table - .select(watermarks::checkpoint_hi) + .select(watermarks::checkpoint_hi_inclusive) .filter(watermarks::entity.eq("objects_snapshot")) .first::(&mut connection) .await @@ -422,9 +422,9 @@ impl PgIndexerStore { }) } - async fn persist_objects_snapshot_chunk( + async fn persist_object_snapshot_mutation_chunk( &self, - objects_snapshot: Vec, + objects_snapshot_mutations: Vec, ) -> Result<(), IndexerError> { use diesel_async::RunQueryDsl; let guard = self @@ -433,11 +433,11 @@ impl PgIndexerStore { .start_timer(); transaction_with_retry(&self.pool, PG_DB_COMMIT_SLEEP_DURATION, |conn| { async { - for objects_snapshot_chunk in - objects_snapshot.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + for mutation_chunk in + objects_snapshot_mutations.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { diesel::insert_into(objects_snapshot::table) - .values(objects_snapshot_chunk) + .values(mutation_chunk) .on_conflict(objects_snapshot::object_id) .do_update() .set(( @@ -482,6 +482,57 @@ impl PgIndexerStore { }) } + async fn persist_object_snapshot_deletion_chunk( + &self, + objects_snapshot_deletions: Vec, + ) -> Result<(), IndexerError> { + use diesel_async::RunQueryDsl; + let guard = self + .metrics + .checkpoint_db_commit_latency_objects_snapshot_chunks + .start_timer(); + + transaction_with_retry(&self.pool, PG_DB_COMMIT_SLEEP_DURATION, |conn| { + async { + for deletion_chunk in + objects_snapshot_deletions.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::delete( + objects_snapshot::table.filter( + objects_snapshot::object_id.eq_any( + deletion_chunk + .iter() + .map(|o| o.object_id.clone()) + .collect::>(), + ), + ), + ) + .execute(conn) + .await + .map_err(IndexerError::from) + .context("Failed to write object deletion to PostgresDB")?; + } + Ok::<(), IndexerError>(()) + } + .scope_boxed() + }) + .await + .tap_ok(|_| { + let elapsed = guard.stop_and_record(); + info!( + elapsed, + "Deleted {} chunked object snapshots", + objects_snapshot_deletions.len(), + ); + }) + .tap_err(|e| { + tracing::error!( + "Failed to persist object snapshot deletions with error: {}", + e + ); + }) + } + async fn persist_objects_history_chunk( &self, stored_objects_history: Vec, @@ -563,12 +614,17 @@ impl PgIndexerStore { }) } - async fn persist_object_version_chunk( + async fn persist_objects_version_chunk( &self, object_versions: Vec, ) -> Result<(), IndexerError> { use diesel_async::RunQueryDsl; + let guard = self + .metrics + .checkpoint_db_commit_latency_objects_version_chunks + .start_timer(); + transaction_with_retry(&self.pool, PG_DB_COMMIT_SLEEP_DURATION, |conn| { async { for object_version_chunk in object_versions.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) @@ -586,6 +642,17 @@ impl PgIndexerStore { .scope_boxed() }) .await + .tap_ok(|_| { + let elapsed = guard.stop_and_record(); + info!( + elapsed, + "Persisted {} chunked object versions", + object_versions.len(), + ); + }) + .tap_err(|e| { + tracing::error!("Failed to persist object versions with error: {}", e); + }) } async fn persist_raw_checkpoints_impl( @@ -831,7 +898,10 @@ impl PgIndexerStore { .do_update() .set(( packages::package_id.eq(excluded(packages::package_id)), + packages::package_version.eq(excluded(packages::package_version)), packages::move_package.eq(excluded(packages::move_package)), + packages::checkpoint_sequence_number + .eq(excluded(packages::checkpoint_sequence_number)), )) .execute(conn) .await?; @@ -910,48 +980,73 @@ impl PgIndexerStore { transaction_with_retry(&self.pool, PG_DB_COMMIT_SLEEP_DURATION, |conn| { async { - diesel::insert_into(event_emit_package::table) - .values(&event_emit_packages) - .on_conflict_do_nothing() - .execute(conn) - .await?; - - diesel::insert_into(event_emit_module::table) - .values(&event_emit_modules) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_emit_packages_chunk in + event_emit_packages.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_emit_package::table) + .values(event_emit_packages_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(event_senders::table) - .values(&event_senders) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_emit_modules_chunk in + event_emit_modules.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_emit_module::table) + .values(event_emit_modules_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(event_struct_package::table) - .values(&event_struct_packages) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_senders_chunk in event_senders.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(event_senders::table) + .values(event_senders_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(event_struct_module::table) - .values(&event_struct_modules) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_struct_packages_chunk in + event_struct_packages.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_struct_package::table) + .values(event_struct_packages_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(event_struct_name::table) - .values(&event_struct_names) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_struct_modules_chunk in + event_struct_modules.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_struct_module::table) + .values(event_struct_modules_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(event_struct_instantiation::table) - .values(&event_struct_instantiations) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for event_struct_names_chunk in + event_struct_names.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_struct_name::table) + .values(event_struct_names_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } + for event_struct_instantiations_chunk in + event_struct_instantiations.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(event_struct_instantiation::table) + .values(event_struct_instantiations_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } Ok(()) } .scope_boxed() @@ -974,8 +1069,6 @@ impl PgIndexerStore { let ( affected_addresses, affected_objects, - senders, - recipients, input_objects, changed_objects, pkgs, @@ -994,14 +1087,10 @@ impl PgIndexerStore { Vec::new(), Vec::new(), Vec::new(), - Vec::new(), - Vec::new(), ), |( mut tx_affected_addresses, mut tx_affected_objects, - mut tx_senders, - mut tx_recipients, mut tx_input_objects, mut tx_changed_objects, mut tx_pkgs, @@ -1013,20 +1102,16 @@ impl PgIndexerStore { index| { tx_affected_addresses.extend(index.0); tx_affected_objects.extend(index.1); - tx_senders.extend(index.2); - tx_recipients.extend(index.3); - tx_input_objects.extend(index.4); - tx_changed_objects.extend(index.5); - tx_pkgs.extend(index.6); - tx_mods.extend(index.7); - tx_funs.extend(index.8); - tx_digests.extend(index.9); - tx_kinds.extend(index.10); + tx_input_objects.extend(index.2); + tx_changed_objects.extend(index.3); + tx_pkgs.extend(index.4); + tx_mods.extend(index.5); + tx_funs.extend(index.6); + tx_digests.extend(index.7); + tx_kinds.extend(index.8); ( tx_affected_addresses, tx_affected_objects, - tx_senders, - tx_recipients, tx_input_objects, tx_changed_objects, tx_pkgs, @@ -1040,71 +1125,83 @@ impl PgIndexerStore { transaction_with_retry(&self.pool, PG_DB_COMMIT_SLEEP_DURATION, |conn| { async { - diesel::insert_into(tx_affected_addresses::table) - .values(&affected_addresses) - .on_conflict_do_nothing() - .execute(conn) - .await?; - - diesel::insert_into(tx_affected_objects::table) - .values(&affected_objects) - .on_conflict_do_nothing() - .execute(conn) - .await?; - - diesel::insert_into(tx_senders::table) - .values(&senders) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for affected_addresses_chunk in + affected_addresses.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(tx_affected_addresses::table) + .values(affected_addresses_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_recipients::table) - .values(&recipients) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for affected_objects_chunk in + affected_objects.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(tx_affected_objects::table) + .values(affected_objects_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_input_objects::table) - .values(&input_objects) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for input_objects_chunk in input_objects.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_input_objects::table) + .values(input_objects_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_changed_objects::table) - .values(&changed_objects) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for changed_objects_chunk in + changed_objects.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + diesel::insert_into(tx_changed_objects::table) + .values(changed_objects_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_calls_pkg::table) - .values(&pkgs) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for pkgs_chunk in pkgs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_calls_pkg::table) + .values(pkgs_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_calls_mod::table) - .values(&mods) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for mods_chunk in mods.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_calls_mod::table) + .values(mods_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_calls_fun::table) - .values(&funs) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for funs_chunk in funs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_calls_fun::table) + .values(funs_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_digests::table) - .values(&digests) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for digests_chunk in digests.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_digests::table) + .values(digests_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } - diesel::insert_into(tx_kinds::table) - .values(&kinds) - .on_conflict_do_nothing() - .execute(conn) - .await?; + for kinds_chunk in kinds.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + diesel::insert_into(tx_kinds::table) + .values(kinds_chunk) + .on_conflict_do_nothing() + .execute(conn) + .await?; + } Ok(()) } @@ -1358,20 +1455,6 @@ impl PgIndexerStore { .execute(conn) .await?; - diesel::delete( - tx_senders::table - .filter(tx_senders::tx_sequence_number.between(min_tx, max_tx)), - ) - .execute(conn) - .await?; - - diesel::delete( - tx_recipients::table - .filter(tx_recipients::tx_sequence_number.between(min_tx, max_tx)), - ) - .execute(conn) - .await?; - diesel::delete( tx_input_objects::table .filter(tx_input_objects::tx_sequence_number.between(min_tx, max_tx)), @@ -1484,9 +1567,10 @@ impl PgIndexerStore { .on_conflict(watermarks::entity) .do_update() .set(( - watermarks::epoch_hi.eq(excluded(watermarks::epoch_hi)), - watermarks::checkpoint_hi.eq(excluded(watermarks::checkpoint_hi)), - watermarks::tx_hi.eq(excluded(watermarks::tx_hi)), + watermarks::epoch_hi_inclusive.eq(excluded(watermarks::epoch_hi_inclusive)), + watermarks::checkpoint_hi_inclusive + .eq(excluded(watermarks::checkpoint_hi_inclusive)), + watermarks::tx_hi_inclusive.eq(excluded(watermarks::tx_hi_inclusive)), )) .execute(conn) .await @@ -1563,36 +1647,27 @@ impl IndexerStore for PgIndexerStore { let mutation_futures = object_mutation_chunks .into_iter() .map(|c| self.persist_object_mutation_chunk(c)) - .collect::>(); - futures::future::join_all(mutation_futures) - .await - .into_iter() - .collect::, _>>() - .map_err(|e| { - IndexerError::PostgresWriteError(format!( - "Failed to persist all object mutation chunks: {:?}", - e - )) - })?; + .map(Either::Left); let deletion_futures = object_deletion_chunks .into_iter() .map(|c| self.persist_object_deletion_chunk(c)) - .collect::>(); - futures::future::join_all(deletion_futures) + .map(Either::Right); + let all_futures = mutation_futures.chain(deletion_futures).collect::>(); + + futures::future::join_all(all_futures) .await .into_iter() .collect::, _>>() .map_err(|e| { IndexerError::PostgresWriteError(format!( - "Failed to persist all object deletion chunks: {:?}", + "Failed to persist all object mutation or deletion chunks: {:?}", e )) })?; - let elapsed = guard.stop_and_record(); info!( elapsed, - "Persisted objects with {} mutations and {} deletions ", mutation_len, deletion_len, + "Persisted {} objects mutations and {} deletions", mutation_len, deletion_len ); Ok(()) } @@ -1609,34 +1684,63 @@ impl IndexerStore for PgIndexerStore { .checkpoint_db_commit_latency_objects_snapshot .start_timer(); let (indexed_mutations, indexed_deletions) = retain_latest_indexed_objects(object_changes); - let objects_snapshot = indexed_mutations + let object_snapshot_mutations: Vec = indexed_mutations .into_iter() .map(StoredObjectSnapshot::from) - .chain( - indexed_deletions - .into_iter() - .map(StoredObjectSnapshot::from), - ) + .collect(); + let object_snapshot_deletions: Vec = indexed_deletions + .into_iter() + .map(StoredObjectSnapshot::from) + .collect(); + let mutation_len = object_snapshot_mutations.len(); + let deletion_len = object_snapshot_deletions.len(); + let object_snapshot_mutation_chunks = chunk!( + object_snapshot_mutations, + self.config.parallel_objects_chunk_size + ); + let object_snapshot_deletion_chunks = chunk!( + object_snapshot_deletions, + self.config.parallel_objects_chunk_size + ); + let mutation_futures = object_snapshot_mutation_chunks + .into_iter() + .map(|c| self.persist_object_snapshot_mutation_chunk(c)) + .map(Either::Left) .collect::>(); - let len = objects_snapshot.len(); - let chunks = chunk!(objects_snapshot, self.config.parallel_objects_chunk_size); - let futures = chunks + let deletion_futures = object_snapshot_deletion_chunks .into_iter() - .map(|c| self.persist_objects_snapshot_chunk(c)) + .map(|c| self.persist_object_snapshot_deletion_chunk(c)) + .map(Either::Right) .collect::>(); - - futures::future::join_all(futures) + let all_futures = mutation_futures + .into_iter() + .chain(deletion_futures) + .collect::>(); + futures::future::join_all(all_futures) .await .into_iter() .collect::, _>>() .map_err(|e| { IndexerError::PostgresWriteError(format!( - "Failed to persist all objects snapshot chunks: {:?}", + "Failed to persist object snapshot mutation or deletion chunks: {:?}", e )) + }) + .tap_ok(|_| { + let elapsed = guard.stop_and_record(); + info!( + elapsed, + "Persisted {} objects snapshot mutations and {} deletions", + mutation_len, + deletion_len + ); + }) + .tap_err(|e| { + tracing::error!( + "Failed to persist object snapshot mutation or deletion chunks: {:?}", + e + ) })?; - let elapsed = guard.stop_and_record(); - info!(elapsed, "Persisted {} objects snapshot", len); Ok(()) } @@ -1740,18 +1844,24 @@ impl IndexerStore for PgIndexerStore { Ok(()) } - async fn persist_object_versions( + async fn persist_objects_version( &self, object_versions: Vec, ) -> Result<(), IndexerError> { if object_versions.is_empty() { return Ok(()); } - let object_versions_count = object_versions.len(); + + let guard = self + .metrics + .checkpoint_db_commit_latency_objects_version + .start_timer(); + + let len = object_versions.len(); let chunks = chunk!(object_versions, self.config.parallel_objects_chunk_size); let futures = chunks .into_iter() - .map(|c| self.persist_object_version_chunk(c)) + .map(|c| self.persist_objects_version_chunk(c)) .collect::>(); futures::future::join_all(futures) @@ -1760,11 +1870,13 @@ impl IndexerStore for PgIndexerStore { .collect::, _>>() .map_err(|e| { IndexerError::PostgresWriteError(format!( - "Failed to persist all object version chunks: {:?}", + "Failed to persist all objects version chunks: {:?}", e )) })?; - info!("Persisted {} object versions", object_versions_count); + + let elapsed = guard.stop_and_record(); + info!(elapsed, "Persisted {} object versions", len); Ok(()) } @@ -1878,9 +1990,12 @@ impl IndexerStore for PgIndexerStore { "Failed to persist all event_indices chunks: {:?}", e )) - })?; - let elapsed = guard.stop_and_record(); - info!(elapsed, "Persisted {} event_indices chunks", len); + }) + .tap_ok(|_| { + let elapsed = guard.stop_and_record(); + info!(elapsed, "Persisted {} event_indices chunks", len); + }) + .tap_err(|e| tracing::error!("Failed to persist all event_indices chunks: {:?}", e))?; Ok(()) } @@ -1908,9 +2023,12 @@ impl IndexerStore for PgIndexerStore { "Failed to persist all tx_indices chunks: {:?}", e )) - })?; - let elapsed = guard.stop_and_record(); - info!(elapsed, "Persisted {} tx_indices chunks", len); + }) + .tap_ok(|_| { + let elapsed = guard.stop_and_record(); + info!(elapsed, "Persisted {} tx_indices chunks", len); + }) + .tap_err(|e| tracing::error!("Failed to persist all tx_indices chunks: {:?}", e))?; Ok(()) } diff --git a/crates/sui-indexer/src/types.rs b/crates/sui-indexer/src/types.rs index 33bca14214125..c7628a593e14a 100644 --- a/crates/sui-indexer/src/types.rs +++ b/crates/sui-indexer/src/types.rs @@ -1,8 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::errors::IndexerError; use move_core_types::language_storage::StructTag; +use rand::Rng; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use sui_json_rpc_types::{ @@ -25,6 +25,8 @@ use sui_types::sui_serde::SuiStructTag; use sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary; use sui_types::transaction::SenderSignedData; +use crate::errors::IndexerError; + pub type IndexerResult = Result; #[derive(Debug, Default)] @@ -254,6 +256,24 @@ pub struct EventIndex { pub type_instantiation: String, } +// for ingestion test +impl EventIndex { + pub fn random() -> Self { + let mut rng = rand::thread_rng(); + EventIndex { + tx_sequence_number: rng.gen(), + event_sequence_number: rng.gen(), + sender: SuiAddress::random_for_testing_only(), + emit_package: ObjectID::random(), + emit_module: rng.gen::().to_string(), + type_package: ObjectID::random(), + type_module: rng.gen::().to_string(), + type_name: rng.gen::().to_string(), + type_instantiation: rng.gen::().to_string(), + } + } +} + impl EventIndex { pub fn from_event( tx_sequence_number: u64, @@ -350,6 +370,25 @@ pub struct IndexedObject { pub df_kind: Option, } +impl IndexedObject { + pub fn random() -> Self { + let mut rng = rand::thread_rng(); + let random_address = SuiAddress::random_for_testing_only(); + IndexedObject { + checkpoint_sequence_number: rng.gen(), + object: Object::with_owner_for_testing(random_address), + df_kind: { + let random_value = rng.gen_range(0..3); + match random_value { + 0 => Some(DynamicFieldType::DynamicField), + 1 => Some(DynamicFieldType::DynamicObject), + _ => None, + } + }, + } + } +} + impl IndexedObject { pub fn from_object( checkpoint_sequence_number: CheckpointSequenceNumber, @@ -371,6 +410,17 @@ pub struct IndexedDeletedObject { pub checkpoint_sequence_number: u64, } +impl IndexedDeletedObject { + pub fn random() -> Self { + let mut rng = rand::thread_rng(); + IndexedDeletedObject { + object_id: ObjectID::random(), + object_version: rng.gen(), + checkpoint_sequence_number: rng.gen(), + } + } +} + #[derive(Debug)] pub struct IndexedPackage { pub package_id: ObjectID, @@ -414,6 +464,41 @@ pub struct TxIndex { pub move_calls: Vec<(ObjectID, String, String)>, } +impl TxIndex { + pub fn random() -> Self { + let mut rng = rand::thread_rng(); + TxIndex { + tx_sequence_number: rng.gen(), + tx_kind: if rng.gen_bool(0.5) { + TransactionKind::SystemTransaction + } else { + TransactionKind::ProgrammableTransaction + }, + transaction_digest: TransactionDigest::random(), + checkpoint_sequence_number: rng.gen(), + input_objects: (0..1000).map(|_| ObjectID::random()).collect(), + changed_objects: (0..1000).map(|_| ObjectID::random()).collect(), + affected_objects: (0..1000).map(|_| ObjectID::random()).collect(), + payers: (0..rng.gen_range(0..100)) + .map(|_| SuiAddress::random_for_testing_only()) + .collect(), + sender: SuiAddress::random_for_testing_only(), + recipients: (0..rng.gen_range(0..1000)) + .map(|_| SuiAddress::random_for_testing_only()) + .collect(), + move_calls: (0..rng.gen_range(0..1000)) + .map(|_| { + ( + ObjectID::random(), + rng.gen::().to_string(), + rng.gen::().to_string(), + ) + }) + .collect(), + } + } +} + // ObjectChange is not bcs deserializable, IndexedObjectChange is. #[serde_as] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] diff --git a/crates/sui-indexer/tests/ingestion_tests.rs b/crates/sui-indexer/tests/ingestion_tests.rs index 57eaaa7286d5a..f2207d5091783 100644 --- a/crates/sui-indexer/tests/ingestion_tests.rs +++ b/crates/sui-indexer/tests/ingestion_tests.rs @@ -7,11 +7,18 @@ use diesel::QueryDsl; use diesel_async::RunQueryDsl; use simulacrum::Simulacrum; use sui_indexer::errors::IndexerError; +use sui_indexer::handlers::TransactionObjectChangesToCommit; use sui_indexer::models::{ - objects::StoredObject, objects::StoredObjectSnapshot, transactions::StoredTransaction, + checkpoints::StoredCheckpoint, objects::StoredObject, objects::StoredObjectSnapshot, + transactions::StoredTransaction, }; -use sui_indexer::schema::{objects, objects_snapshot, transactions}; +use sui_indexer::schema::{checkpoints, objects, objects_snapshot, transactions}; +use sui_indexer::store::indexer_store::IndexerStore; use sui_indexer::test_utils::{set_up, wait_for_checkpoint, wait_for_objects_snapshot}; +use sui_indexer::types::EventIndex; +use sui_indexer::types::IndexedDeletedObject; +use sui_indexer::types::IndexedObject; +use sui_indexer::types::TxIndex; use sui_types::base_types::SuiAddress; use sui_types::effects::TransactionEffectsAPI; use sui_types::gas_coin::GasCoin; @@ -174,3 +181,93 @@ pub async fn test_objects_snapshot() -> Result<(), IndexerError> { assert_eq!(snapshot_object.owner_id, Some(gas_owner_id.to_vec())); Ok(()) } + +#[tokio::test] +pub async fn test_objects_ingestion() -> Result<(), IndexerError> { + let tempdir = tempdir().unwrap(); + let mut sim = Simulacrum::new(); + let data_ingestion_path = tempdir.path().to_path_buf(); + sim.set_data_ingestion_path(data_ingestion_path.clone()); + + let (_, pg_store, _, _database) = set_up(Arc::new(sim), data_ingestion_path).await; + + let mut objects = Vec::new(); + for _ in 0..1000 { + objects.push(TransactionObjectChangesToCommit { + changed_objects: vec![IndexedObject::random()], + deleted_objects: vec![IndexedDeletedObject::random()], + }); + } + pg_store.persist_objects(objects).await?; + Ok(()) +} + +// test insert large batch of tx_indices +#[tokio::test] +pub async fn test_insert_large_batch_tx_indices() -> Result<(), IndexerError> { + let tempdir = tempdir().unwrap(); + let mut sim = Simulacrum::new(); + let data_ingestion_path = tempdir.path().to_path_buf(); + sim.set_data_ingestion_path(data_ingestion_path.clone()); + + let (_, pg_store, _, _database) = set_up(Arc::new(sim), data_ingestion_path).await; + + let mut v = Vec::new(); + for _ in 0..1000 { + v.push(TxIndex::random()); + } + pg_store.persist_tx_indices(v).await?; + Ok(()) +} + +// test insert large batch of event_indices +#[tokio::test] +pub async fn test_insert_large_batch_event_indices() -> Result<(), IndexerError> { + let tempdir = tempdir().unwrap(); + let mut sim = Simulacrum::new(); + let data_ingestion_path = tempdir.path().to_path_buf(); + sim.set_data_ingestion_path(data_ingestion_path.clone()); + + let (_, pg_store, _, _database) = set_up(Arc::new(sim), data_ingestion_path).await; + + let mut v = Vec::new(); + for _ in 0..1000 { + v.push(EventIndex::random()); + } + pg_store.persist_event_indices(v).await?; + Ok(()) +} + +#[tokio::test] +pub async fn test_epoch_boundary() -> Result<(), IndexerError> { + println!("test_epoch_boundary"); + let tempdir = tempdir().unwrap(); + let mut sim = Simulacrum::new(); + let data_ingestion_path = tempdir.path().to_path_buf(); + sim.set_data_ingestion_path(data_ingestion_path.clone()); + + let transfer_recipient = SuiAddress::random_for_testing_only(); + let (transaction, _) = sim.transfer_txn(transfer_recipient); + let (_, err) = sim.execute_transaction(transaction.clone()).unwrap(); + assert!(err.is_none()); + + sim.create_checkpoint(); // checkpoint 1 + sim.advance_epoch(true); // checkpoint 2 and epoch 1 + + let (transaction, _) = sim.transfer_txn(transfer_recipient); + let (_, err) = sim.execute_transaction(transaction.clone()).unwrap(); + sim.create_checkpoint(); // checkpoint 3 + assert!(err.is_none()); + + let (_, pg_store, _, _database) = set_up(Arc::new(sim), data_ingestion_path).await; + wait_for_checkpoint(&pg_store, 3).await?; + let mut connection = pg_store.pool().dedicated_connection().await.unwrap(); + let db_checkpoint: StoredCheckpoint = checkpoints::table + .order(checkpoints::sequence_number.desc()) + .first::(&mut connection) + .await + .expect("Failed reading checkpoint from PostgresDB"); + assert_eq!(db_checkpoint.sequence_number, 3); + assert_eq!(db_checkpoint.epoch, 1); + Ok(()) +} diff --git a/crates/sui-json-rpc-api/src/deepbook.rs b/crates/sui-json-rpc-api/src/deepbook.rs new file mode 100644 index 0000000000000..4f2f098918a3e --- /dev/null +++ b/crates/sui-json-rpc-api/src/deepbook.rs @@ -0,0 +1,14 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use jsonrpsee::core::RpcResult; +use jsonrpsee::proc_macros::rpc; + +use sui_open_rpc_macros::open_rpc; + +#[open_rpc(namespace = "suix", tag = "DeepBook Read API")] +#[rpc(server, client, namespace = "suix")] +pub trait DeepBookApi { + #[method(name = "ping")] + async fn ping(&self) -> RpcResult; +} diff --git a/crates/sui-json-rpc-types/src/sui_event.rs b/crates/sui-json-rpc-types/src/sui_event.rs index b9e36f5adfb5f..86aab9194a608 100644 --- a/crates/sui-json-rpc-types/src/sui_event.rs +++ b/crates/sui-json-rpc-types/src/sui_event.rs @@ -205,6 +205,10 @@ fn try_into_byte(v: &Value) -> Option { pub enum EventFilter { /// Return all events. All([Box; 0]), + + /// Return events that match any of the given filters. Only supported on event subscriptions. + Any(Vec), + /// Query by sender address. Sender(SuiAddress), /// Return events emitted by the given transaction. @@ -263,6 +267,7 @@ impl Filter for EventFilter { let _scope = monitored_scope("EventFilter::matches"); match self { EventFilter::All([]) => true, + EventFilter::Any(filters) => filters.iter().any(|f| f.matches(item)), EventFilter::MoveEventType(event_type) => &item.type_ == event_type, EventFilter::Sender(sender) => &item.sender == sender, EventFilter::MoveModule { package, module } => { diff --git a/crates/sui-json-rpc/src/error.rs b/crates/sui-json-rpc/src/error.rs index 793671849ed9e..d4abdb05dfe06 100644 --- a/crates/sui-json-rpc/src/error.rs +++ b/crates/sui-json-rpc/src/error.rs @@ -554,8 +554,9 @@ mod tests { #[test] fn test_quorum_driver_internal_error() { - let quorum_driver_error = - QuorumDriverError::QuorumDriverInternalError(SuiError::UnexpectedMessage); + let quorum_driver_error = QuorumDriverError::QuorumDriverInternalError( + SuiError::UnexpectedMessage("test".to_string()), + ); let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); @@ -570,7 +571,7 @@ mod tests { fn test_system_overload() { let quorum_driver_error = QuorumDriverError::SystemOverload { overloaded_stake: 10, - errors: vec![(SuiError::UnexpectedMessage, 0, vec![])], + errors: vec![(SuiError::UnexpectedMessage("test".to_string()), 0, vec![])], }; let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 3ff42aa0eac2d..6a0446837c8f2 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1293,7 +1293,7 @@ "name": "Result", "value": { "minSupportedProtocolVersion": "1", - "maxSupportedProtocolVersion": "62", + "maxSupportedProtocolVersion": "64", "protocolVersion": "6", "featureFlags": { "accept_zklogin_in_multisig": false, @@ -1592,6 +1592,7 @@ "u64": "2" }, "execution_version": null, + "gas_budget_based_txn_cost_cap_factor": null, "gas_model_version": { "u64": "5" }, @@ -6120,6 +6121,22 @@ }, "additionalProperties": false }, + { + "description": "Return events that match any of the given filters. Only supported on event subscriptions.", + "type": "object", + "required": [ + "Any" + ], + "properties": { + "Any": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventFilter" + } + } + }, + "additionalProperties": false + }, { "description": "Query by sender address.", "type": "object", diff --git a/crates/sui-package-resolver/src/lib.rs b/crates/sui-package-resolver/src/lib.rs index 828e6ea96d8e4..3c21640cad7a5 100644 --- a/crates/sui-package-resolver/src/lib.rs +++ b/crates/sui-package-resolver/src/lib.rs @@ -348,6 +348,33 @@ impl Resolver { } impl Resolver { + /// The canonical form of a type refers to each type in terms of its defining package ID. This + /// function takes a non-canonical type and updates all its package IDs to the appropriate + /// defining ID. + /// + /// For every `package::module::datatype` in the input `tag`, `package` must be an object + /// on-chain, containing a move package that includes `module`, and that module must define the + /// `datatype`. In practice this means the input type `tag` can refer to types at or after + /// their defining IDs. + pub async fn canonical_type(&self, mut tag: TypeTag) -> Result { + let mut context = ResolutionContext::new(self.limits.as_ref()); + + // (1). Fetch all the information from this store that is necessary to relocate package IDs + // in the type. + context + .add_type_tag( + &mut tag, + &self.package_store, + /* visit_fields */ false, + /* visit_phantoms */ true, + ) + .await?; + + // (2). Use that information to relocate package IDs in the type. + context.canonicalize_type(&mut tag)?; + Ok(tag) + } + /// Return the type layout corresponding to the given type tag. The layout always refers to /// structs in terms of their defining ID (i.e. their package ID always points to the first /// package that introduced them). @@ -1349,6 +1376,36 @@ impl<'l> ResolutionContext<'l> { Ok(()) } + /// Translate runtime IDs in a type `tag` into defining IDs using only the information + /// contained in this context. Requires that the necessary information was added to the context + /// through calls to `add_type_tag`. + fn canonicalize_type(&self, tag: &mut TypeTag) -> Result<()> { + use TypeTag as T; + + match tag { + T::Signer => return Err(Error::UnexpectedSigner), + T::Address | T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 => { + /* nop */ + } + + T::Vector(tag) => self.canonicalize_type(tag.as_mut())?, + + T::Struct(s) => { + for tag in &mut s.type_params { + self.canonicalize_type(tag)?; + } + + // SAFETY: `add_type_tag` ensures `datatyps` has an element with this key. + let key = DatatypeRef::from(s.as_ref()); + let def = &self.datatypes[&key]; + + s.address = def.defining_id; + } + } + + Ok(()) + } + /// Translate a type `tag` into its layout using only the information contained in this context. /// Requires that the necessary information was added to the context through calls to /// `add_type_tag` and `add_signature` before being called. @@ -1404,7 +1461,7 @@ impl<'l> ResolutionContext<'l> { .type_params .iter() // Reduce the max depth because we know these type parameters will be nested - // wthin this struct. + // within this struct. .map(|tag| self.resolve_type_layout(tag, max_depth - 1)) .collect::>>()?; @@ -1737,9 +1794,90 @@ mod tests { format!("struct:\n{struct_layout:#}\n\nenum:\n{enum_layout:#}",) } + #[tokio::test] + async fn test_simple_canonical_type() { + let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); + let package_resolver = Resolver::new(cache); + + let input = type_("0xa0::m::T0"); + let expect = input.clone(); + let actual = package_resolver.canonical_type(input).await.unwrap(); + assert_eq!(expect, actual); + } + + #[tokio::test] + async fn test_upgraded_canonical_type() { + let (_, cache) = package_cache([ + (1, build_package("a0"), a0_types()), + (2, build_package("a1"), a1_types()), + ]); + + let package_resolver = Resolver::new(cache); + + let input = type_("0xa1::m::T3"); + let expect = input.clone(); + let actual = package_resolver.canonical_type(input).await.unwrap(); + assert_eq!(expect, actual); + } + + #[tokio::test] + async fn test_latest_canonical_type() { + let (_, cache) = package_cache([ + (1, build_package("a0"), a0_types()), + (2, build_package("a1"), a1_types()), + ]); + + let package_resolver = Resolver::new(cache); + + let input = type_("0xa1::m::T0"); + let expect = type_("0xa0::m::T0"); + let actual = package_resolver.canonical_type(input).await.unwrap(); + assert_eq!(expect, actual); + } + + #[tokio::test] + async fn test_type_param_canonical_type() { + let (_, cache) = package_cache([ + (1, build_package("a0"), a0_types()), + (2, build_package("a1"), a1_types()), + ]); + + let package_resolver = Resolver::new(cache); + + let input = type_("0xa1::m::T1<0xa1::m::T0, 0xa1::m::T3>"); + let expect = type_("0xa0::m::T1<0xa0::m::T0, 0xa1::m::T3>"); + let actual = package_resolver.canonical_type(input).await.unwrap(); + assert_eq!(expect, actual); + } + + #[tokio::test] + async fn test_canonical_err_package_too_old() { + let (_, cache) = package_cache([ + (1, build_package("a0"), a0_types()), + (2, build_package("a1"), a1_types()), + ]); + + let package_resolver = Resolver::new(cache); + + let input = type_("0xa0::m::T3"); + let err = package_resolver.canonical_type(input).await.unwrap_err(); + assert!(matches!(err, Error::DatatypeNotFound(_, _, _))); + } + + #[tokio::test] + async fn test_canonical_err_signer() { + let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); + + let package_resolver = Resolver::new(cache); + + let input = type_("0xa0::m::T1<0xa0::m::T0, signer>"); + let err = package_resolver.canonical_type(input).await.unwrap_err(); + assert!(matches!(err, Error::UnexpectedSigner)); + } + /// Layout for a type that only refers to base types or other types in the same module. #[tokio::test] - async fn test_simple_type() { + async fn test_simple_type_layout() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let package_resolver = Resolver::new(cache); let struct_layout = package_resolver @@ -1755,7 +1893,7 @@ mod tests { /// A type that refers to types from other modules in the same package. #[tokio::test] - async fn test_cross_module() { + async fn test_cross_module_layout() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new(cache); let struct_layout = resolver.type_layout(type_("0xa0::n::T0")).await.unwrap(); @@ -1765,7 +1903,7 @@ mod tests { /// A type that refers to types a different package. #[tokio::test] - async fn test_cross_package() { + async fn test_cross_package_layout() { let (_, cache) = package_cache([ (1, build_package("a0"), a0_types()), (1, build_package("b0"), b0_types()), @@ -1780,7 +1918,7 @@ mod tests { /// A type from an upgraded package, mixing structs defined in the original package and the /// upgraded package. #[tokio::test] - async fn test_upgraded_package() { + async fn test_upgraded_package_layout() { let (_, cache) = package_cache([ (1, build_package("a0"), a0_types()), (2, build_package("a1"), a1_types()), @@ -1795,7 +1933,7 @@ mod tests { /// A generic type instantiation where the type parameters are resolved relative to linkage /// contexts from different versions of the same package. #[tokio::test] - async fn test_multiple_linkage_contexts() { + async fn test_multiple_linkage_contexts_layout() { let (_, cache) = package_cache([ (1, build_package("a0"), a0_types()), (2, build_package("a1"), a1_types()), @@ -1818,7 +1956,7 @@ mod tests { /// type can be referred to using the ID of any package that declares it, rather than only the /// package that first declared it (whose ID is its defining ID). #[tokio::test] - async fn test_upgraded_package_non_defining_id() { + async fn test_upgraded_package_non_defining_id_layout() { let (_, cache) = package_cache([ (1, build_package("a0"), a0_types()), (2, build_package("a1"), a1_types()), @@ -1840,7 +1978,7 @@ mod tests { /// dependency on A from v1 to v2. The type in C refers to types that were defined in both B, A /// v1, and A v2. #[tokio::test] - async fn test_relinking() { + async fn test_relinking_layout() { let (_, cache) = package_cache([ (1, build_package("a0"), a0_types()), (2, build_package("a1"), a1_types()), @@ -1855,7 +1993,7 @@ mod tests { } #[tokio::test] - async fn test_value_nesting_boundary() { + async fn test_value_nesting_boundary_layout() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new_with_limits( @@ -1881,7 +2019,7 @@ mod tests { } #[tokio::test] - async fn test_err_value_nesting_simple() { + async fn test_err_value_nesting_simple_layout() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new_with_limits( @@ -1908,7 +2046,7 @@ mod tests { } #[tokio::test] - async fn test_err_value_nesting_big_type_param() { + async fn test_err_value_nesting_big_type_param_layout() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new_with_limits( @@ -1936,7 +2074,7 @@ mod tests { } #[tokio::test] - async fn test_err_value_nesting_big_phantom_type_param() { + async fn test_err_value_nesting_big_phantom_type_param_layout() { let (_, cache) = package_cache([ (1, build_package("sui"), sui_types()), (1, build_package("d0"), d0_types()), @@ -1978,7 +2116,7 @@ mod tests { } #[tokio::test] - async fn test_err_value_nesting_type_param_application() { + async fn test_err_value_nesting_type_param_application_layout() { let (_, cache) = package_cache([ (1, build_package("sui"), sui_types()), (1, build_package("d0"), d0_types()), @@ -2093,7 +2231,7 @@ mod tests { } #[tokio::test] - async fn test_err_not_a_package() { + async fn test_layout_err_not_a_package() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new(cache); let err = resolver @@ -2104,7 +2242,7 @@ mod tests { } #[tokio::test] - async fn test_err_no_module() { + async fn test_layout_err_no_module() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new(cache); let err = resolver @@ -2115,7 +2253,7 @@ mod tests { } #[tokio::test] - async fn test_err_no_struct() { + async fn test_layout_err_no_struct() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new(cache); @@ -2127,7 +2265,7 @@ mod tests { } #[tokio::test] - async fn test_err_type_arity() { + async fn test_layout_err_type_arity() { let (_, cache) = package_cache([(1, build_package("a0"), a0_types())]); let resolver = Resolver::new(cache); diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module_enum.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module_enum.snap deleted file mode 100644 index bdfb0231e4f0b..0000000000000 --- a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module_enum.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: crates/sui-package-resolver/src/lib.rs -expression: "format!(\"{layout:#}\")" ---- -enum 0xa0::n::E0 { - V0 { - t: enum 0xa0::m::E1 { - V { - a: address, - p: u16, - q: vector, - }, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - l: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - }, -} diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_module_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package_enum.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package_enum.snap deleted file mode 100644 index a64e236c8470d..0000000000000 --- a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package_enum.snap +++ /dev/null @@ -1,110 +0,0 @@ ---- -source: crates/sui-package-resolver/src/lib.rs -expression: "format!(\"{layout:#}\")" ---- -enum 0xb0::m::E0 { - V0 { - m: struct 0xa0::m::T2 { - x: u8, - }, - n: struct 0xa0::n::T0 { - t: struct 0xa0::m::T1 { - a: address, - p: u16, - q: vector, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - }, - em: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - en: enum 0xa0::n::E0 { - V0 { - t: enum 0xa0::m::E1 { - V { - a: address, - p: u16, - q: vector, - }, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - l: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - }, - }, - }, - V1 { - em: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - en: enum 0xa0::n::E0 { - V0 { - t: enum 0xa0::m::E1 { - V { - a: address, - p: u16, - q: vector, - }, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - l: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - }, - }, - }, - V2 { - m: struct 0xa0::m::T2 { - x: u8, - }, - n: struct 0xa0::n::T0 { - t: struct 0xa0::m::T1 { - a: address, - p: u16, - q: vector, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - }, - em: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - en: enum 0xa0::n::E0 { - V0 { - t: enum 0xa0::m::E1 { - V { - a: address, - p: u16, - q: vector, - }, - }, - u: struct 0xa0::m::T2 { - x: u8, - }, - l: enum 0xa0::m::E2 { - V0 { - x: u8, - }, - }, - }, - }, - }, -} diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__cross_package_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__multiple_linkage_contexts.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__multiple_linkage_contexts_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__multiple_linkage_contexts.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__multiple_linkage_contexts_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__relinking.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__relinking_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__relinking.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__relinking_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type_layout.snap similarity index 89% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type_layout.snap index 0c4a04da2661b..fb0d5509afc12 100644 --- a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type.snap +++ b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__simple_type_layout.snap @@ -1,6 +1,6 @@ --- source: crates/sui-package-resolver/src/lib.rs -expression: "format!(\"struct:\\n{struct_layout:#}\\n\\nenum:\\n{enum_layout:#}\")" +expression: "fmt(struct_layout, enum_layout)" --- struct: struct 0xa0::m::T0 { diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_enum.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_enum.snap deleted file mode 100644 index 8fe1d2ffb0707..0000000000000 --- a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_enum.snap +++ /dev/null @@ -1,34 +0,0 @@ ---- -source: crates/sui-package-resolver/src/lib.rs -expression: "format!(\"{layout:#}\")" ---- -enum 0xa1::n::E1 { - V0 { - t: struct 0xa0::m::T1<0xa1::m::T3, u32> { - a: address, - p: struct 0xa1::m::T3 { - y: u16, - }, - q: vector, - }, - u: struct 0xa1::m::T4 { - z: u32, - }, - et: enum 0xa0::m::E1<0xa1::m::E3, u32> { - V { - a: address, - p: enum 0xa1::m::E3 { - V0 { - y: u16, - }, - }, - q: vector, - }, - }, - eu: enum 0xa1::m::E4 { - V0 { - z: u32, - }, - }, - }, -} diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_non_defining_id.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_non_defining_id_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_non_defining_id.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__upgraded_package_non_defining_id_layout.snap diff --git a/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__value_nesting_boundary.snap b/crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__value_nesting_boundary_layout.snap similarity index 100% rename from crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__value_nesting_boundary.snap rename to crates/sui-package-resolver/src/snapshots/sui_package_resolver__tests__value_nesting_boundary_layout.snap diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index e4132460f68f4..67eee3da37382 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -18,7 +18,7 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -const MAX_PROTOCOL_VERSION: u64 = 62; +const MAX_PROTOCOL_VERSION: u64 = 64; // Record history of protocol version allocations here: // @@ -184,6 +184,8 @@ const MAX_PROTOCOL_VERSION: u64 = 62; // Further reduce minimum number of random beacon shares. // Add feature flag for Mysticeti fastpath. // Version 62: Makes the event's sending module package upgrade-aware. +// Version 63: Enable gas based congestion control in consensus commit. +// Version 64: Switch to distributed vote scoring in consensus in mainnet #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -577,8 +579,9 @@ impl ConsensusTransactionOrdering { pub enum PerObjectCongestionControlMode { #[default] None, // No congestion control. - TotalGasBudget, // Use txn gas budget as execution cost. - TotalTxCount, // Use total txn count as execution cost. + TotalGasBudget, // Use txn gas budget as execution cost. + TotalTxCount, // Use total txn count as execution cost. + TotalGasBudgetWithCap, // Use txn gas budget as execution cost with a cap. } impl PerObjectCongestionControlMode { @@ -1265,6 +1268,11 @@ pub struct ProtocolConfig { /// Configures the garbage collection depth for consensus. When is unset or `0` then the garbage collection /// is disabled. consensus_gc_depth: Option, + + /// Used to calculate the max transaction cost when using TotalGasBudgetWithCap as shard + /// object congestion control strategy. Basically the max transaction cost is calculated as + /// (num of input object + num of commands) * this factor. + gas_budget_based_txn_cost_cap_factor: Option, } // feature flags @@ -1539,10 +1547,6 @@ impl ProtocolConfig { self.feature_flags.consensus_network } - pub fn mysticeti_leader_scoring_and_schedule(&self) -> bool { - self.feature_flags.mysticeti_leader_scoring_and_schedule - } - pub fn reshare_at_same_initial_version(&self) -> bool { self.feature_flags.reshare_at_same_initial_version } @@ -2139,6 +2143,8 @@ impl ProtocolConfig { max_accumulated_txn_cost_per_object_in_mysticeti_commit: None, consensus_gc_depth: None, + + gas_budget_based_txn_cost_cap_factor: None, // When adding a new constant, set it to None in the earliest version, like this: // new_constant: None, }; @@ -2820,6 +2826,18 @@ impl ProtocolConfig { 62 => { cfg.feature_flags.relocate_event_module = true; } + 63 => { + cfg.feature_flags.per_object_congestion_control_mode = + PerObjectCongestionControlMode::TotalGasBudgetWithCap; + cfg.gas_budget_based_txn_cost_cap_factor = Some(400_000); + cfg.max_accumulated_txn_cost_per_object_in_mysticeti_commit = Some(18_500_000); + cfg.max_accumulated_txn_cost_per_object_in_narwhal_commit = Some(240_000_000); + } + 64 => { + // Enable distributed vote scoring for mainnet + cfg.feature_flags + .consensus_distributed_vote_scoring_strategy = true; + } // Use this template when making changes: // // // modify an existing constant. @@ -2956,14 +2974,11 @@ impl ProtocolConfig { pub fn set_zklogin_max_epoch_upper_bound_delta_for_testing(&mut self, val: Option) { self.feature_flags.zklogin_max_epoch_upper_bound_delta = val } + pub fn set_disable_bridge_for_testing(&mut self) { self.feature_flags.bridge = false } - pub fn set_mysticeti_leader_scoring_and_schedule_for_testing(&mut self, val: bool) { - self.feature_flags.mysticeti_leader_scoring_and_schedule = val; - } - pub fn set_mysticeti_num_leaders_per_round_for_testing(&mut self, val: Option) { self.feature_flags.mysticeti_num_leaders_per_round = val; } diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_63.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_63.snap new file mode 100644 index 0000000000000..14410e41af35a --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_63.snap @@ -0,0 +1,328 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 63 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_64.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_64.snap new file mode 100644 index 0000000000000..a7a8858f62cab --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_64.snap @@ -0,0 +1,329 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 64 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_63.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_63.snap new file mode 100644 index 0000000000000..785270a636a42 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_63.snap @@ -0,0 +1,329 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 63 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_64.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_64.snap new file mode 100644 index 0000000000000..a7a8858f62cab --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_64.snap @@ -0,0 +1,329 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 64 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_63.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_63.snap new file mode 100644 index 0000000000000..c004a35dfdee9 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_63.snap @@ -0,0 +1,339 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 63 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_poseidon: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + enable_group_ops_native_function_msm: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + enable_vdf: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + passkey_auth: true + authority_capabilities_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + mysticeti_fastpath: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +poseidon_bn254_cost_base: 260 +poseidon_bn254_cost_per_block: 10 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +vdf_verify_vdf_cost: 1500 +vdf_hash_to_input_cost: 100 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_64.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_64.snap new file mode 100644 index 0000000000000..870c932d2378c --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_64.snap @@ -0,0 +1,339 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 64 +feature_flags: + package_upgrades: true + commit_root_state_digest: true + advance_epoch_start_time_in_safe_mode: true + loaded_child_objects_fixed: true + missing_type_is_compatibility_error: true + scoring_decision_with_validity_cutoff: true + consensus_order_end_of_epoch_last: true + disallow_adding_abilities_on_upgrade: true + disable_invariant_violation_check_in_swap_loc: true + advance_to_highest_supported_protocol_version: true + ban_entry_init: true + package_digest_hash_module: true + disallow_change_struct_type_params_on_upgrade: true + no_extraneous_module_bytes: true + narwhal_versioned_metadata: true + zklogin_auth: true + consensus_transaction_ordering: ByGasPrice + simplified_unwrap_then_delete: true + upgraded_multisig_supported: true + txn_base_cost_as_multiplier: true + shared_object_deletion: true + narwhal_new_leader_election_schedule: true + loaded_child_object_format: true + enable_jwk_consensus_updates: true + end_of_epoch_transaction_supported: true + simple_conservation_checks: true + loaded_child_object_format_type: true + receive_objects: true + random_beacon: true + bridge: true + enable_effects_v2: true + narwhal_certificate_v2: true + verify_legacy_zklogin_address: true + recompute_has_public_transfer_in_execution: true + accept_zklogin_in_multisig: true + include_consensus_digest_in_prologue: true + hardened_otw_check: true + allow_receiving_object_id: true + enable_poseidon: true + enable_coin_deny_list: true + enable_group_ops_native_functions: true + enable_group_ops_native_function_msm: true + reject_mutable_random_on_entry_functions: true + per_object_congestion_control_mode: TotalGasBudgetWithCap + consensus_choice: Mysticeti + consensus_network: Tonic + zklogin_max_epoch_upper_bound_delta: 30 + mysticeti_leader_scoring_and_schedule: true + reshare_at_same_initial_version: true + resolve_abort_locations_to_package_id: true + mysticeti_use_committed_subdag_digest: true + enable_vdf: true + record_consensus_determined_version_assignments_in_prologue: true + fresh_vm_on_framework_upgrade: true + prepend_prologue_tx_in_consensus_commit_in_checkpoints: true + mysticeti_num_leaders_per_round: 1 + soft_bundle: true + enable_coin_deny_list_v2: true + passkey_auth: true + authority_capabilities_v2: true + rethrow_serialization_type_layout_errors: true + consensus_distributed_vote_scoring_strategy: true + consensus_round_prober: true + validate_identifier_inputs: true + mysticeti_fastpath: true + relocate_event_module: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 +gas_model_version: 8 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 9900 +storage_fund_reinvest_rate: 500 +reward_slashing_rate: 10000 +storage_gas_price: 76 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +poseidon_bn254_cost_base: 260 +poseidon_bn254_cost_per_block: 10 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +vdf_verify_vdf_cost: 1500 +vdf_hash_to_input_cost: 100 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 3 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 700 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 240000000 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 18500000 +gas_budget_based_txn_cost_cap_factor: 400000 diff --git a/crates/sui-rest-api/Cargo.toml b/crates/sui-rest-api/Cargo.toml index fb9c54846c33e..01d163356eec1 100644 --- a/crates/sui-rest-api/Cargo.toml +++ b/crates/sui-rest-api/Cargo.toml @@ -32,6 +32,7 @@ fastcrypto.workspace = true sui-types.workspace = true mysten-network.workspace = true sui-protocol-config.workspace = true +move-binary-format.workspace = true [dev-dependencies] diffy = "0.3" diff --git a/crates/sui-rest-api/openapi/openapi.json b/crates/sui-rest-api/openapi/openapi.json index fd84c7f6ae460..58a4d5841a7e9 100644 --- a/crates/sui-rest-api/openapi/openapi.json +++ b/crates/sui-rest-api/openapi/openapi.json @@ -884,6 +884,137 @@ } } }, + "/transactions/simulate": { + "post": { + "tags": [ + "Transactions" + ], + "description": "[![unstable](https://img.shields.io/badge/api-unstable-red?style=for-the-badge)](#) _Api subject to change; use at your own risk_\n\n", + "operationId": "SimulateTransaction", + "parameters": [ + { + "in": "query", + "name": "balance_changes", + "description": "Request `BalanceChanges` be included in the Response.", + "schema": { + "description": "Request `BalanceChanges` be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + }, + { + "in": "query", + "name": "input_objects", + "description": "Request input `Object`s be included in the Response.", + "schema": { + "description": "Request input `Object`s be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + }, + { + "in": "query", + "name": "output_objects", + "description": "Request output `Object`s be included in the Response.", + "schema": { + "description": "Request output `Object`s be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + } + ], + "requestBody": { + "content": { + "application/bcs": {} + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionSimulationResponse" + } + }, + "application/bcs": {} + } + } + } + } + }, + "/transactions/resolve": { + "post": { + "tags": [ + "Transactions" + ], + "description": "[![unstable](https://img.shields.io/badge/api-unstable-red?style=for-the-badge)](#) _Api subject to change; use at your own risk_\n\n", + "operationId": "ResolveTransaction", + "parameters": [ + { + "in": "query", + "name": "balance_changes", + "description": "Request `BalanceChanges` be included in the Response.", + "schema": { + "description": "Request `BalanceChanges` be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + }, + { + "in": "query", + "name": "input_objects", + "description": "Request input `Object`s be included in the Response.", + "schema": { + "description": "Request input `Object`s be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + }, + { + "in": "query", + "name": "output_objects", + "description": "Request output `Object`s be included in the Response.", + "schema": { + "description": "Request output `Object`s be included in the Response.", + "default": "false", + "type": "boolean" + }, + "style": "form" + }, + { + "in": "query", + "name": "simulate", + "description": "Request that the fully resolved transaction be simulated and have its results sent back in the response.", + "schema": { + "description": "Request that the fully resolved transaction be simulated and have its results sent back in the response.", + "default": false, + "type": "boolean" + }, + "style": "form" + } + ], + "requestBody": {}, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResolveTransactionResponse" + } + }, + "application/bcs": {} + } + } + } + } + }, "/coins/{coin_type}": { "get": { "tags": [ @@ -4027,6 +4158,21 @@ } } }, + "ResolveTransactionResponse": { + "description": "Response type for the execute transaction endpoint", + "type": "object", + "required": [ + "transaction" + ], + "properties": { + "simulation": { + "$ref": "#/components/schemas/TransactionSimulationResponse" + }, + "transaction": { + "$ref": "#/components/schemas/Transaction" + } + } + }, "Secp256k1PublicKey": { "description": "Base64 encoded data", "type": "string", @@ -4047,24 +4193,6 @@ "type": "string", "format": "base64" }, - "SignedTransaction": { - "type": "object", - "required": [ - "signatures", - "transaction" - ], - "properties": { - "signatures": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserSignature" - } - }, - "transaction": { - "$ref": "#/components/schemas/Transaction" - } - } - }, "SimpleSignature": { "oneOf": [ { @@ -5173,6 +5301,39 @@ } } }, + "TransactionSimulationResponse": { + "description": "Response type for the transaction simulation endpoint", + "type": "object", + "required": [ + "effects" + ], + "properties": { + "balance_changes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BalanceChange" + } + }, + "effects": { + "$ref": "#/components/schemas/TransactionEffects" + }, + "events": { + "$ref": "#/components/schemas/TransactionEvents" + }, + "input_objects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Object" + } + }, + "output_objects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Object" + } + } + } + }, "TypeArgumentError": { "oneOf": [ { diff --git a/crates/sui-rest-api/src/client/mod.rs b/crates/sui-rest-api/src/client/mod.rs index 4afc853fabfd0..1ee1f5d804682 100644 --- a/crates/sui-rest-api/src/client/mod.rs +++ b/crates/sui-rest-api/src/client/mod.rs @@ -30,6 +30,10 @@ impl Client { } } + pub fn inner(&self) -> &sdk::Client { + &self.inner + } + pub async fn get_latest_checkpoint(&self) -> Result { self.inner .get_latest_checkpoint() diff --git a/crates/sui-rest-api/src/client/sdk.rs b/crates/sui-rest-api/src/client/sdk.rs index e327765632116..f5e94ef898c58 100644 --- a/crates/sui-rest-api/src/client/sdk.rs +++ b/crates/sui-rest-api/src/client/sdk.rs @@ -14,7 +14,9 @@ use sui_sdk_types::types::ObjectId; use sui_sdk_types::types::SignedCheckpointSummary; use sui_sdk_types::types::SignedTransaction; use sui_sdk_types::types::StructTag; +use sui_sdk_types::types::Transaction; use sui_sdk_types::types::TransactionDigest; +use sui_sdk_types::types::UnresolvedTransaction; use sui_sdk_types::types::ValidatorCommittee; use sui_sdk_types::types::Version; use tap::Pipe; @@ -34,8 +36,11 @@ use crate::system::SystemStateSummary; use crate::system::X_SUI_MAX_SUPPORTED_PROTOCOL_VERSION; use crate::system::X_SUI_MIN_SUPPORTED_PROTOCOL_VERSION; use crate::transactions::ListTransactionsQueryParameters; +use crate::transactions::ResolveTransactionQueryParameters; +use crate::transactions::ResolveTransactionResponse; use crate::transactions::TransactionExecutionResponse; use crate::transactions::TransactionResponse; +use crate::transactions::TransactionSimulationResponse; use crate::types::X_SUI_CHAIN; use crate::types::X_SUI_CHAIN_ID; use crate::types::X_SUI_CHECKPOINT_HEIGHT; @@ -406,6 +411,62 @@ impl Client { self.bcs(response).await } + pub async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> Result> { + let url = self.url().join("transactions/simulate")?; + + let body = bcs::to_bytes(transaction)?; + + let response = self + .inner + .post(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .header(reqwest::header::CONTENT_TYPE, crate::APPLICATION_BCS) + .body(body) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn resolve_transaction( + &self, + unresolved_transaction: &UnresolvedTransaction, + ) -> Result> { + let url = self.url.join("transactions/resolve")?; + + let response = self + .inner + .post(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .json(unresolved_transaction) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn resolve_transaction_with_parameters( + &self, + unresolved_transaction: &UnresolvedTransaction, + parameters: &ResolveTransactionQueryParameters, + ) -> Result> { + let url = self.url.join("transactions/resolve")?; + + let response = self + .inner + .post(url) + .query(¶meters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .json(unresolved_transaction) + .send() + .await?; + + self.bcs(response).await + } + async fn check_response( &self, response: reqwest::Response, diff --git a/crates/sui-rest-api/src/lib.rs b/crates/sui-rest-api/src/lib.rs index 21a438529e3eb..3cf1e49952efe 100644 --- a/crates/sui-rest-api/src/lib.rs +++ b/crates/sui-rest-api/src/lib.rs @@ -93,6 +93,8 @@ const ENDPOINTS: &[&dyn ApiEndpoint] = &[ &system::GetProtocolConfig, &system::GetGasInfo, &transactions::ExecuteTransaction, + &transactions::SimulateTransaction, + &transactions::ResolveTransaction, &coins::GetCoinInfo, ]; diff --git a/crates/sui-rest-api/src/transactions/execution.rs b/crates/sui-rest-api/src/transactions/execution.rs index e10e788ea2325..d1994b83e4d40 100644 --- a/crates/sui-rest-api/src/transactions/execution.rs +++ b/crates/sui-rest-api/src/transactions/execution.rs @@ -1,26 +1,24 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::net::SocketAddr; -use std::sync::Arc; - +use crate::openapi::{ + ApiEndpoint, OperationBuilder, RequestBodyBuilder, ResponseBuilder, RouteHandler, +}; +use crate::response::Bcs; +use crate::{accept::AcceptFormat, response::ResponseContent}; +use crate::{RestError, RestService, Result}; use axum::extract::{Query, State}; use schemars::JsonSchema; +use std::net::SocketAddr; +use std::sync::Arc; use sui_sdk_types::types::framework::Coin; use sui_sdk_types::types::{ Address, BalanceChange, CheckpointSequenceNumber, Object, Owner, SignedTransaction, - TransactionEffects, TransactionEvents, ValidatorAggregatedSignature, + Transaction, TransactionEffects, TransactionEvents, ValidatorAggregatedSignature, }; -use sui_types::transaction_executor::TransactionExecutor; +use sui_types::transaction_executor::{SimulateTransactionResult, TransactionExecutor}; use tap::Pipe; -use crate::openapi::{ - ApiEndpoint, OperationBuilder, RequestBodyBuilder, ResponseBuilder, RouteHandler, -}; -use crate::response::Bcs; -use crate::{accept::AcceptFormat, response::ResponseContent}; -use crate::{RestService, Result}; - pub struct ExecuteTransaction; impl ApiEndpoint for ExecuteTransaction { @@ -36,8 +34,6 @@ impl ApiEndpoint for ExecuteTransaction { &self, generator: &mut schemars::gen::SchemaGenerator, ) -> openapiv3::v3_1::Operation { - generator.subschema_for::(); - OperationBuilder::new() .tag("Transactions") .operation_id("ExecuteTransaction") @@ -172,7 +168,7 @@ async fn execute_transaction( } /// Query parameters for the execute transaction endpoint -#[derive(Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +#[derive(Debug, Default, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct ExecuteTransactionQueryParameters { // TODO once transaction finality support is more fully implemented up and down the stack, add // back in this parameter, which will be mutally-exclusive with the other parameters. When @@ -356,3 +352,135 @@ fn derive_balance_changes( }) .collect() } + +pub struct SimulateTransaction; + +impl ApiEndpoint for SimulateTransaction { + fn method(&self) -> axum::http::Method { + axum::http::Method::POST + } + + fn path(&self) -> &'static str { + "/transactions/simulate" + } + + fn operation( + &self, + generator: &mut schemars::gen::SchemaGenerator, + ) -> openapiv3::v3_1::Operation { + OperationBuilder::new() + .tag("Transactions") + .operation_id("SimulateTransaction") + .query_parameters::(generator) + .request_body(RequestBodyBuilder::new().bcs_content().build()) + .response( + 200, + ResponseBuilder::new() + .json_content::(generator) + .bcs_content() + .build(), + ) + .build() + } + + fn handler(&self) -> RouteHandler { + RouteHandler::new(self.method(), simulate_transaction) + } +} + +async fn simulate_transaction( + State(state): State>>, + Query(parameters): Query, + accept: AcceptFormat, + //TODO allow accepting JSON as well as BCS + Bcs(transaction): Bcs, +) -> Result> { + let executor = state.ok_or_else(|| anyhow::anyhow!("No Transaction Executor"))?; + + simulate_transaction_impl(&executor, ¶meters, transaction).map(|response| match accept { + AcceptFormat::Json => ResponseContent::Json(response), + AcceptFormat::Bcs => ResponseContent::Bcs(response), + }) +} + +pub(super) fn simulate_transaction_impl( + executor: &Arc, + parameters: &SimulateTransactionQueryParameters, + transaction: Transaction, +) -> Result { + if transaction.gas_payment.objects.is_empty() { + return Err(RestError::new( + axum::http::StatusCode::BAD_REQUEST, + "no gas payment provided", + )); + } + + let SimulateTransactionResult { + input_objects, + output_objects, + events, + effects, + mock_gas_id, + } = executor + .simulate_transaction(transaction.try_into()?) + .map_err(anyhow::Error::from)?; + + if mock_gas_id.is_some() { + return Err(RestError::new( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + "simulate unexpectedly used a mock gas payment", + )); + } + + let events = events.map(TryInto::try_into).transpose()?; + let effects = effects.try_into()?; + + let input_objects = input_objects + .into_values() + .map(TryInto::try_into) + .collect::, _>>()?; + let output_objects = output_objects + .into_values() + .map(TryInto::try_into) + .collect::, _>>()?; + let balance_changes = derive_balance_changes(&effects, &input_objects, &output_objects); + + TransactionSimulationResponse { + events, + effects, + balance_changes: parameters.balance_changes.then_some(balance_changes), + input_objects: parameters.input_objects.then_some(input_objects), + output_objects: parameters.output_objects.then_some(output_objects), + } + .pipe(Ok) +} + +/// Response type for the transaction simulation endpoint +#[derive(Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct TransactionSimulationResponse { + pub effects: TransactionEffects, + pub events: Option, + pub balance_changes: Option>, + pub input_objects: Option>, + pub output_objects: Option>, +} + +/// Query parameters for the simulate transaction endpoint +#[derive(Debug, Default, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct SimulateTransactionQueryParameters { + /// Request `BalanceChanges` be included in the Response. + #[serde(default)] + #[serde(with = "serde_with::As::")] + #[schemars(with = "bool")] + pub balance_changes: bool, + /// Request input `Object`s be included in the Response. + #[serde(default)] + #[serde(with = "serde_with::As::")] + #[schemars(with = "bool")] + pub input_objects: bool, + /// Request output `Object`s be included in the Response. + #[serde(default)] + #[serde(with = "serde_with::As::")] + #[schemars(with = "bool")] + pub output_objects: bool, +} diff --git a/crates/sui-rest-api/src/transactions/mod.rs b/crates/sui-rest-api/src/transactions/mod.rs index 2ff7ce754392f..362aa4c40ed2c 100644 --- a/crates/sui-rest-api/src/transactions/mod.rs +++ b/crates/sui-rest-api/src/transactions/mod.rs @@ -5,7 +5,15 @@ mod execution; pub use execution::EffectsFinality; pub use execution::ExecuteTransaction; pub use execution::ExecuteTransactionQueryParameters; +pub use execution::SimulateTransaction; +pub use execution::SimulateTransactionQueryParameters; pub use execution::TransactionExecutionResponse; +pub use execution::TransactionSimulationResponse; + +mod resolve; +pub use resolve::ResolveTransaction; +pub use resolve::ResolveTransactionQueryParameters; +pub use resolve::ResolveTransactionResponse; use axum::extract::{Path, Query, State}; use axum::http::StatusCode; diff --git a/crates/sui-rest-api/src/transactions/resolve.rs b/crates/sui-rest-api/src/transactions/resolve.rs new file mode 100644 index 0000000000000..b1e69cbb5ee43 --- /dev/null +++ b/crates/sui-rest-api/src/transactions/resolve.rs @@ -0,0 +1,604 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use super::execution::SimulateTransactionQueryParameters; +use super::TransactionSimulationResponse; +use crate::accept::AcceptFormat; +use crate::objects::ObjectNotFoundError; +use crate::openapi::ApiEndpoint; +use crate::openapi::OperationBuilder; +use crate::openapi::RequestBodyBuilder; +use crate::openapi::ResponseBuilder; +use crate::openapi::RouteHandler; +use crate::reader::StateReader; +use crate::response::ResponseContent; +use crate::RestError; +use crate::RestService; +use crate::Result; +use axum::extract::Query; +use axum::extract::State; +use axum::Json; +use itertools::Itertools; +use move_binary_format::normalized; +use schemars::JsonSchema; +use sui_protocol_config::ProtocolConfig; +use sui_sdk_types::types::Argument; +use sui_sdk_types::types::Command; +use sui_sdk_types::types::ObjectId; +use sui_sdk_types::types::Transaction; +use sui_sdk_types::types::UnresolvedInputArgument; +use sui_sdk_types::types::UnresolvedObjectReference; +use sui_sdk_types::types::UnresolvedProgrammableTransaction; +use sui_sdk_types::types::UnresolvedTransaction; +use sui_types::base_types::ObjectID; +use sui_types::base_types::ObjectRef; +use sui_types::base_types::SuiAddress; +use sui_types::effects::TransactionEffectsAPI; +use sui_types::gas::GasCostSummary; +use sui_types::gas_coin::GasCoin; +use sui_types::move_package::MovePackage; +use sui_types::transaction::CallArg; +use sui_types::transaction::GasData; +use sui_types::transaction::ObjectArg; +use sui_types::transaction::ProgrammableTransaction; +use sui_types::transaction::TransactionData; +use sui_types::transaction::TransactionDataAPI; +use tap::Pipe; + +// TODO +// - Updating the UnresolvedTransaction format to provide less information about inputs +// - handle basic type inference and BCS serialization of pure args +pub struct ResolveTransaction; + +impl ApiEndpoint for ResolveTransaction { + fn method(&self) -> axum::http::Method { + axum::http::Method::POST + } + + fn path(&self) -> &'static str { + "/transactions/resolve" + } + + fn operation( + &self, + generator: &mut schemars::gen::SchemaGenerator, + ) -> openapiv3::v3_1::Operation { + OperationBuilder::new() + .tag("Transactions") + .operation_id("ResolveTransaction") + .query_parameters::(generator) + .request_body( + RequestBodyBuilder::new() + // .json_content::(generator) + .build(), + ) + .response( + 200, + ResponseBuilder::new() + .json_content::(generator) + .bcs_content() + .build(), + ) + .build() + } + + fn handler(&self) -> RouteHandler { + RouteHandler::new(self.method(), resolve_transaction) + } +} + +async fn resolve_transaction( + State(state): State, + Query(parameters): Query, + accept: AcceptFormat, + Json(unresolved_transaction): Json, +) -> Result> { + let executor = state + .executor + .as_ref() + .ok_or_else(|| anyhow::anyhow!("No Transaction Executor"))?; + let (reference_gas_price, protocol_config) = { + let system_state = state.reader.get_system_state_summary()?; + + let current_protocol_version = state.reader.get_system_state_summary()?.protocol_version; + + let protocol_config = ProtocolConfig::get_for_version_if_supported( + current_protocol_version.into(), + state.reader.inner().get_chain_identifier()?.chain(), + ) + .ok_or_else(|| { + RestError::new( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + "unable to get current protocol config", + ) + })?; + + (system_state.reference_gas_price, protocol_config) + }; + let called_packages = + called_packages(&state.reader, &protocol_config, &unresolved_transaction)?; + let user_provided_budget = unresolved_transaction + .gas_payment + .as_ref() + .and_then(|payment| payment.budget); + let mut resolved_transaction = resolve_unresolved_transaction( + &state.reader, + &called_packages, + reference_gas_price, + protocol_config.max_tx_gas(), + unresolved_transaction, + )?; + + // If the user didn't provide a budget we need to run a quick simulation in order to calculate + // a good estimated budget to use + let budget = if let Some(user_provided_budget) = user_provided_budget { + user_provided_budget + } else { + let simulation_result = executor + .simulate_transaction(resolved_transaction.clone()) + .map_err(anyhow::Error::from)?; + + let estimate = estimate_gas_budget_from_gas_cost( + simulation_result.effects.gas_cost_summary(), + reference_gas_price, + ); + resolved_transaction.gas_data_mut().budget = estimate; + estimate + }; + + // If the user didn't provide any gas payment we need to do gas selection now + if resolved_transaction.gas_data().payment.is_empty() { + let input_objects = resolved_transaction + .input_objects() + .map_err(anyhow::Error::from)? + .iter() + .flat_map(|obj| match obj { + sui_types::transaction::InputObjectKind::ImmOrOwnedMoveObject((id, _, _)) => { + Some(*id) + } + _ => None, + }) + .collect_vec(); + let gas_coins = select_gas( + &state.reader, + resolved_transaction.gas_data().owner, + budget, + protocol_config.max_gas_payment_objects(), + &input_objects, + )?; + resolved_transaction.gas_data_mut().payment = gas_coins; + } + + let simulation = if parameters.simulate { + super::execution::simulate_transaction_impl( + executor, + ¶meters.simulate_transaction_parameters, + resolved_transaction.clone().try_into()?, + )? + .pipe(Some) + } else { + None + }; + + ResolveTransactionResponse { + transaction: resolved_transaction.try_into()?, + simulation, + } + .pipe(|response| match accept { + AcceptFormat::Json => ResponseContent::Json(response), + AcceptFormat::Bcs => ResponseContent::Bcs(response), + }) + .pipe(Ok) +} + +/// Query parameters for the resolve transaction endpoint +#[derive(Debug, Default, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct ResolveTransactionQueryParameters { + /// Request that the fully resolved transaction be simulated and have its results sent back in + /// the response. + #[serde(default)] + pub simulate: bool, + #[serde(flatten)] + pub simulate_transaction_parameters: SimulateTransactionQueryParameters, +} + +struct NormalizedPackage { + #[allow(unused)] + package: MovePackage, + normalized_modules: BTreeMap, +} + +fn called_packages( + reader: &StateReader, + protocol_config: &ProtocolConfig, + unresolved_transaction: &UnresolvedTransaction, +) -> Result> { + let binary_config = sui_types::execution_config_utils::to_binary_config(protocol_config); + let mut packages = HashMap::new(); + + for move_call in unresolved_transaction + .ptb + .commands + .iter() + .filter_map(|command| { + if let Command::MoveCall(move_call) = command { + Some(move_call) + } else { + None + } + }) + { + let package = reader + .inner() + .get_object(&(move_call.package.into()))? + .ok_or_else(|| ObjectNotFoundError::new(move_call.package))? + .data + .try_as_package() + .ok_or_else(|| { + RestError::new( + axum::http::StatusCode::BAD_REQUEST, + format!("object {} is not a package", move_call.package), + ) + })? + .to_owned(); + + // Normalization doesn't take the linkage or type origin tables into account, which means + // that if you have an upgraded package that introduces a new type, then that type's + // package ID is going to appear incorrectly if you fetch it from its normalized module. + // + // Despite the above this is safe given we are only using the signature information (and in + // particular the reference kind) from the normalized package. + let normalized_modules = package.normalize(&binary_config).map_err(|e| { + RestError::new( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + format!("unable to normalize package {}: {e}", move_call.package), + ) + })?; + let package = NormalizedPackage { + package, + normalized_modules, + }; + + packages.insert(move_call.package, package); + } + + Ok(packages) +} + +fn resolve_unresolved_transaction( + reader: &StateReader, + called_packages: &HashMap, + reference_gas_price: u64, + max_gas_budget: u64, + unresolved_transaction: UnresolvedTransaction, +) -> Result { + let sender = unresolved_transaction.sender.into(); + let gas_data = if let Some(unresolved_gas_payment) = unresolved_transaction.gas_payment { + let payment = unresolved_gas_payment + .objects + .into_iter() + .map(|unresolved| resolve_object_reference(reader, unresolved)) + .collect::>>()?; + GasData { + payment, + owner: unresolved_gas_payment.owner.into(), + price: unresolved_gas_payment.price.unwrap_or(reference_gas_price), + budget: unresolved_gas_payment.budget.unwrap_or(max_gas_budget), + } + } else { + GasData { + payment: vec![], + owner: sender, + price: reference_gas_price, + budget: max_gas_budget, + } + }; + let expiration = unresolved_transaction.expiration.into(); + let ptb = resolve_ptb(reader, called_packages, unresolved_transaction.ptb)?; + Ok(TransactionData::V1( + sui_types::transaction::TransactionDataV1 { + kind: sui_types::transaction::TransactionKind::ProgrammableTransaction(ptb), + sender, + gas_data, + expiration, + }, + )) +} + +/// Response type for the execute transaction endpoint +#[derive(Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct ResolveTransactionResponse { + pub transaction: Transaction, + pub simulation: Option, +} + +fn resolve_object_reference( + reader: &StateReader, + unresolved_object_reference: UnresolvedObjectReference, +) -> Result { + let UnresolvedObjectReference { + object_id, + version, + digest, + } = unresolved_object_reference; + + let id = object_id.into(); + let (v, d) = if let Some(version) = version { + let object = reader + .inner() + .get_object_by_key(&id, version.into())? + .ok_or_else(|| ObjectNotFoundError::new_with_version(object_id, version))?; + (object.version(), object.digest()) + } else { + let object = reader + .inner() + .get_object(&id)? + .ok_or_else(|| ObjectNotFoundError::new(object_id))?; + (object.version(), object.digest()) + }; + + if digest.is_some_and(|digest| digest.inner() != d.inner()) { + return Err(RestError::new( + axum::http::StatusCode::BAD_REQUEST, + format!("provided digest doesn't match, provided: {digest:?} actual: {d}"), + )); + } + + Ok((id, v, d)) +} + +fn resolve_ptb( + reader: &StateReader, + called_packages: &HashMap, + unresolved_ptb: UnresolvedProgrammableTransaction, +) -> Result { + let inputs = unresolved_ptb + .inputs + .into_iter() + .enumerate() + .map(|(arg_idx, arg)| { + resolve_arg( + reader, + called_packages, + &unresolved_ptb.commands, + arg, + arg_idx, + ) + }) + .collect::>()?; + + ProgrammableTransaction { + inputs, + commands: unresolved_ptb + .commands + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + } + .pipe(Ok) +} + +fn resolve_arg( + reader: &StateReader, + called_packages: &HashMap, + commands: &[Command], + arg: UnresolvedInputArgument, + arg_idx: usize, +) -> Result { + match arg { + UnresolvedInputArgument::Pure { value } => CallArg::Pure(value), + UnresolvedInputArgument::ImmutableOrOwned(obj_ref) => CallArg::Object( + ObjectArg::ImmOrOwnedObject(resolve_object_reference(reader, obj_ref)?), + ), + UnresolvedInputArgument::Shared { + object_id, + initial_shared_version: _, + mutable: _, + } => { + let id = object_id.into(); + let object = reader + .inner() + .get_object(&id)? + .ok_or_else(|| ObjectNotFoundError::new(object_id))?; + + let initial_shared_version = if let sui_types::object::Owner::Shared { + initial_shared_version, + } = object.owner() + { + *initial_shared_version + } else { + return Err(RestError::new( + axum::http::StatusCode::BAD_REQUEST, + format!("object {object_id} is not a shared object"), + )); + }; + + let mut mutable = false; + + for (command, idx) in find_arg_uses(arg_idx, commands) { + match (command, idx) { + (Command::MoveCall(move_call), Some(idx)) => { + let function = called_packages + // Find the package + .get(&move_call.package) + // Find the module + .and_then(|package| { + package.normalized_modules.get(move_call.module.as_str()) + }) + // Find the function + .and_then(|module| module.functions.get(move_call.function.as_str())) + .ok_or_else(|| { + RestError::new( + axum::http::StatusCode::BAD_REQUEST, + format!( + "unable to find function {package}::{module}::{function}", + package = move_call.package, + module = move_call.module, + function = move_call.function + ), + ) + })?; + + let arg_type = function.parameters.get(idx).ok_or_else(|| { + RestError::new( + axum::http::StatusCode::BAD_REQUEST, + "invalid input parameter", + ) + })?; + + if matches!( + arg_type, + move_binary_format::normalized::Type::MutableReference(_) + | move_binary_format::normalized::Type::Struct { .. } + ) { + mutable = true; + } + } + + ( + Command::SplitCoins(_) + | Command::MergeCoins(_) + | Command::MakeMoveVector(_), + _, + ) => { + mutable = true; + } + + _ => {} + } + + // Early break out of the loop if we've already determined that the shared object + // is needed to be mutable + if mutable { + break; + } + } + + CallArg::Object(ObjectArg::SharedObject { + id, + initial_shared_version, + mutable, + }) + } + UnresolvedInputArgument::Receiving(obj_ref) => CallArg::Object(ObjectArg::Receiving( + resolve_object_reference(reader, obj_ref)?, + )), + } + .pipe(Ok) +} + +/// Given an particular input argument, find all of its uses. +/// +/// The returned iterator contains all commands where the argument is used and an optional index +/// for where the argument is used in that command. +fn find_arg_uses( + arg_idx: usize, + commands: &[Command], +) -> impl Iterator)> { + commands.iter().filter_map(move |command| { + match command { + Command::MoveCall(move_call) => move_call + .arguments + .iter() + .position(|elem| matches_input_arg(*elem, arg_idx)) + .map(Some), + Command::TransferObjects(transfer_objects) => transfer_objects + .objects + .iter() + .position(|elem| matches_input_arg(*elem, arg_idx)) + .map(Some), + Command::SplitCoins(split_coins) => { + matches_input_arg(split_coins.coin, arg_idx).then_some(None) + } + Command::MergeCoins(merge_coins) => { + if matches_input_arg(merge_coins.coin, arg_idx) { + Some(None) + } else { + merge_coins + .coins_to_merge + .iter() + .position(|elem| matches_input_arg(*elem, arg_idx)) + .map(Some) + } + } + Command::Publish(_) => None, + Command::MakeMoveVector(make_move_vector) => make_move_vector + .elements + .iter() + .position(|elem| matches_input_arg(*elem, arg_idx)) + .map(Some), + Command::Upgrade(upgrade) => matches_input_arg(upgrade.ticket, arg_idx).then_some(None), + } + .map(|x| (command, x)) + }) +} + +fn matches_input_arg(arg: Argument, arg_idx: usize) -> bool { + matches!(arg, Argument::Input(idx) if idx as usize == arg_idx) +} + +/// Estimate the gas budget using the gas_cost_summary from a previous DryRun +/// +/// The estimated gas budget is computed as following: +/// * the maximum between A and B, where: +/// A = computation cost + GAS_SAFE_OVERHEAD * reference gas price +/// B = computation cost + storage cost - storage rebate + GAS_SAFE_OVERHEAD * reference gas price +/// overhead +/// +/// This gas estimate is computed similarly as in the TypeScript SDK +fn estimate_gas_budget_from_gas_cost( + gas_cost_summary: &GasCostSummary, + reference_gas_price: u64, +) -> u64 { + const GAS_SAFE_OVERHEAD: u64 = 1000; + + let safe_overhead = GAS_SAFE_OVERHEAD * reference_gas_price; + let computation_cost_with_overhead = gas_cost_summary.computation_cost + safe_overhead; + + let gas_usage = gas_cost_summary.net_gas_usage() + safe_overhead as i64; + computation_cost_with_overhead.max(if gas_usage < 0 { 0 } else { gas_usage as u64 }) +} + +fn select_gas( + reader: &StateReader, + owner: SuiAddress, + budget: u64, + max_gas_payment_objects: u32, + input_objects: &[ObjectID], +) -> Result> { + let gas_coins = reader + .inner() + .account_owned_objects_info_iter(owner, None)? + .filter(|info| info.type_.is_gas_coin()) + .filter(|info| !input_objects.contains(&info.object_id)) + .filter_map(|info| reader.inner().get_object(&info.object_id).ok().flatten()) + .filter_map(|object| { + GasCoin::try_from(&object) + .ok() + .map(|coin| (object.compute_object_reference(), coin.value())) + }) + .take(max_gas_payment_objects as usize); + + let mut selected_gas = vec![]; + let mut selected_gas_value = 0; + + for (object_ref, value) in gas_coins { + selected_gas.push(object_ref); + selected_gas_value += value; + } + + if selected_gas_value >= budget { + Ok(selected_gas) + } else { + Err(RestError::new( + axum::http::StatusCode::BAD_REQUEST, + format!( + "unable to select sufficient gas coins from account {owner} \ + to satisfy required budget {budget}" + ), + )) + } +} diff --git a/crates/sui-rosetta/src/operations.rs b/crates/sui-rosetta/src/operations.rs index fd1bfd1443223..ced23f58e402b 100644 --- a/crates/sui-rosetta/src/operations.rs +++ b/crates/sui-rosetta/src/operations.rs @@ -651,7 +651,9 @@ impl Operations { .ok_or_else(|| anyhow!("Response balance changes should not be empty."))? { if let Ok(currency) = cache.get_currency(&balance_change.coin_type).await { - balance_changes.push((balance_change.clone(), currency)); + if !currency.symbol.is_empty() { + balance_changes.push((balance_change.clone(), currency)); + } } } diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml new file mode 100644 index 0000000000000..3f5e73de1f5c7 --- /dev/null +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml @@ -0,0 +1,9 @@ +[package] +name = "test_coin" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } + +[addresses] +test_coin = "0x0" \ No newline at end of file diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move new file mode 100644 index 0000000000000..715857a7245da --- /dev/null +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move @@ -0,0 +1,24 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Module: test_coin +module test_coin::test_coin { + + use sui::coin; + + public struct TEST_COIN has drop {} + + fun init(witness: TEST_COIN, ctx: &mut TxContext) { + let (treasury, metadata) = coin::create_currency( + witness, + 6, + b"", + b"", + b"", + option::none(), + ctx + ); + transfer::public_freeze_object(metadata); + transfer::public_transfer(treasury, ctx.sender()) + } +} diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs b/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs index c7e640b268fc1..13ba4aa714af6 100644 --- a/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs @@ -131,8 +131,8 @@ pub async fn init_package( client: &SuiClient, keystore: &Keystore, sender: SuiAddress, + path: &Path, ) -> Result { - let path = Path::new("tests/custom_coins/test_coin"); let path_buf = base::reroot_path(Some(path))?; let move_build_config = MoveBuildConfig::default(); @@ -266,7 +266,14 @@ async fn test_mint() { let keystore = &test_cluster.wallet.config.keystore; let sender = test_cluster.get_address_0(); - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let address1 = test_cluster.get_address_1(); let address2 = test_cluster.get_address_2(); diff --git a/crates/sui-rosetta/tests/custom_coins_tests.rs b/crates/sui-rosetta/tests/custom_coins_tests.rs index 57f6233a69292..77094e35e9d7d 100644 --- a/crates/sui-rosetta/tests/custom_coins_tests.rs +++ b/crates/sui-rosetta/tests/custom_coins_tests.rs @@ -8,15 +8,16 @@ mod test_coin_utils; use serde_json::json; use std::num::NonZeroUsize; +use std::path::Path; use sui_json_rpc_types::{ SuiExecutionStatus, SuiTransactionBlockEffectsAPI, SuiTransactionBlockResponseOptions, }; use sui_rosetta::operations::Operations; -use sui_rosetta::types::Currencies; use sui_rosetta::types::{ AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier, Currency, CurrencyMetadata, NetworkIdentifier, SuiEnv, }; +use sui_rosetta::types::{Currencies, OperationType}; use sui_rosetta::CoinMetadataCache; use sui_rosetta::SUI; use test_cluster::TestClusterBuilder; @@ -37,7 +38,14 @@ async fn test_custom_coin_balance() { let (rosetta_client, _handle) = start_rosetta_test_server(client.clone()).await; let sender = test_cluster.get_address_0(); - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let address1 = test_cluster.get_address_1(); let address2 = test_cluster.get_address_2(); @@ -160,7 +168,14 @@ async fn test_custom_coin_transfer() { let keystore = &test_cluster.wallet.config.keystore; // TEST_COIN setup and mint - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let balances_to = vec![(COIN1_BALANCE, sender)]; let coin_type = init_ret.coin_tag.to_canonical_string(true); let _mint_res = mint(&client, keystore, init_ret, balances_to) @@ -234,3 +249,55 @@ async fn test_custom_coin_transfer() { serde_json::to_string(&ops2).unwrap() ); } + +#[tokio::test] +async fn test_custom_coin_without_symbol() { + const COIN1_BALANCE: u64 = 100_000_000_000_000_000; + let test_cluster = TestClusterBuilder::new().build().await; + let sender = test_cluster.get_address_0(); + let client = test_cluster.wallet.get_client().await.unwrap(); + let keystore = &test_cluster.wallet.config.keystore; + + // TEST_COIN setup and mint + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin_no_symbol"), + ) + .await + .unwrap(); + + let balances_to = vec![(COIN1_BALANCE, sender)]; + let mint_res = mint(&client, keystore, init_ret, balances_to) + .await + .unwrap(); + + let tx = client + .read_api() + .get_transaction_with_options( + mint_res.digest, + SuiTransactionBlockResponseOptions::new() + .with_input() + .with_effects() + .with_balance_changes() + .with_events(), + ) + .await + .unwrap(); + + assert_eq!( + &SuiExecutionStatus::Success, + tx.effects.as_ref().unwrap().status() + ); + let coin_cache = CoinMetadataCache::new(client, NonZeroUsize::new(2).unwrap()); + let ops = Operations::try_from_response(tx, &coin_cache) + .await + .unwrap(); + + for op in ops { + if op.type_ == OperationType::SuiBalanceChange { + assert!(!op.amount.unwrap().currency.symbol.is_empty()) + } + } +} diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap index 64381ec53fa28..f1dce45993ad4 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap @@ -6,7 +6,7 @@ ssfn_config_info: ~ validator_config_info: ~ parameters: chain_start_timestamp_ms: 0 - protocol_version: 62 + protocol_version: 64 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 stake_subsidy_start_epoch: 0 @@ -49,4 +49,3 @@ accounts: - 30000000000000000 - 30000000000000000 - 30000000000000000 - diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__network_config_snapshot_matches.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__network_config_snapshot_matches.snap index 780e3a675a1c0..8557a77990c4c 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__network_config_snapshot_matches.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__network_config_snapshot_matches.snap @@ -102,6 +102,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -113,6 +115,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -143,8 +147,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -159,7 +163,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -270,6 +274,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -281,6 +287,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -311,8 +319,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -327,7 +335,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -438,6 +446,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -449,6 +459,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -479,8 +491,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -495,7 +507,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -606,6 +618,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -617,6 +631,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -647,8 +663,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -663,7 +679,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -774,6 +790,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -785,6 +803,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -815,8 +835,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -831,7 +851,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -942,6 +962,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -953,6 +975,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -983,8 +1007,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -999,7 +1023,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -1110,6 +1134,8 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -1121,6 +1147,8 @@ validator_configs: - Twitch Testnet: - Apple + - Arden + - "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es" - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Credenza3 - Facebook @@ -1151,8 +1179,8 @@ validator_configs: - Twitch authority-overload-config: max-txn-age-in-queue: - secs: 1 - nanos: 0 + secs: 0 + nanos: 200000000 overload-monitor-interval: secs: 10 nanos: 0 @@ -1167,7 +1195,7 @@ validator_configs: safe-transaction-ready-rate: 100 check-system-overload-at-signing: true max-transaction-manager-queue-length: 100000 - max-transaction-manager-per-object-queue-length: 100 + max-transaction-manager-per-object-queue-length: 20 execution-cache: writeback-cache: max_cache_size: ~ @@ -1186,4 +1214,3 @@ account_keys: - mfPjCoE6SX0Sl84MnmNS/LS+tfPpkn7I8tziuk2g0WM= - 5RWlYF22jS9i76zLl8jP2D3D8GC5ht+IP1dWUBGZxi8= genesis: "[fake genesis]" - diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index 5a37904ea3193..05eaa6318c556 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -3,7 +3,7 @@ source: crates/sui-swarm-config/tests/snapshot_tests.rs expression: genesis.sui_system_object().into_genesis_version_for_tooling() --- epoch: 0 -protocol_version: 62 +protocol_version: 64 system_state_version: 1 validators: total_stake: 20000000000000000 @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x4f629d26c57551fd5ccf03e0bd64bb9005687e7d0680508f26dcace639b165ef" + id: "0xcc91d06982135046b4cfa9bbd4f7f206cdaf3f9fb27c782a7137b3bef08892ed" size: 0 voting_power: 10000 - operation_cap_id: "0x2fba4507d8d6ab5b21d44342d3406b5112a82ede7602c1070e7c53ff20521467" + operation_cap_id: "0xfc07728a8857acbf12a2d5684de4f98920fad0952f784426eaafdda79b94ee20" gas_price: 1000 staking_pool: - id: "0xb1ac0597cf93273a32eebf6eb921676365db6d63788321cabdb472a2d5c0b170" + id: "0xdb3034b1953243443f3cbc912d09de43d8e836803c018ba14245d7fa0a4c383d" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0x019ff669614a8fb22bef655912832f39ba675b637930a42ddb65abf19aa6b24c" + id: "0x8d37fa87257f45904f0ef1424ead1cc8e1ea23cf00ff16ce9a63f2b77df6231f" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0xa8f2c5692de4e9f72f8d7d9c70e00c54704f8f232ca28028df668463d5297cb7" + id: "0x9f7ae3a49a73bbfdf1721536325cb5699b3cdb3174e42e461bb4eed6cb45aeb0" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x1ee8eb11345dadf1d88acf5cb26bfdbff625433fe833359510b71b67a773ae02" + id: "0xcf38a9cd46d8931bb0e06800c7dd818023842c6eed78c74f4d3663e3b256e9fe" size: 0 pending_active_validators: contents: - id: "0xddb2e575ff0b5aa6819847fa500e70eef24500cf40524789811a93d771ca0ff2" + id: "0x5757ed8fb231e16f163db4f3a31aa660fccf4189e5c8fe535588abfbabde730f" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x96908877d2a68c389469b4bc1739382761dfeb083a121979e64f55ea4f790695" + id: "0x34c34655c2c9a2f2b708915189cf536c6f0e129655ef50d7157f461d74487741" size: 1 inactive_validators: - id: "0xcf9ee70ea65e35e6fcc751fbaad2b60ab36b0104afcc3c2adea1663f6dd0f74f" + id: "0x15c6d3a2bd25a1232b8114376e0421dbdd5acf92f50bbd091588e7a43d5276da" size: 0 validator_candidates: - id: "0xa5b70ebd266ccf00af5311ba400eb01871e7f3fa423cc7ca5a2c253f5241f40c" + id: "0xb2c748bd9e0c7cc676667f7ea2909b6edc224d8dc1d1797626b926409a5f04ef" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x2d0e822d9c1acada00c0822a9b09058f92b9d7211c85c45998561af90f5bc0ef" + id: "0xfcd0b94a9048c129e2e0e690bc3afe2a9e35377d64821c33076e7c14969b9143" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0xa026eaa35feb14bd1eb1e71f988d42e87d66c85388feaa3f53c3a22b7b3831d8" + id: "0x34e56a96336b73d4983cfdb95c4392f7214fc632973e1529f63ee7caf6ed0458" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0x7eb42ca897dc4f88c6b72f51872657f5b946d925154743d0330ce96ff023787a" + id: "0xbfd2b299dab597fcf4e004a252274bd8262e18521b63168c49eca2b1839dc207" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,6 +332,5 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x061bfc58ee1164bf043c5ce8662126eb8a67936ec682ffd855fd2877212af86f" + id: "0x7f447c7cb0761fe5d2350b00849a94f887b78b2b3a5afd75f97092be04e1bf27" size: 0 - diff --git a/crates/sui-tls/Cargo.toml b/crates/sui-tls/Cargo.toml index 4f1eaea244784..4a03d9c81dc30 100644 --- a/crates/sui-tls/Cargo.toml +++ b/crates/sui-tls/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] anyhow.workspace = true +arc-swap.workspace = true ed25519.workspace = true pkcs8.workspace = true rcgen.workspace = true diff --git a/crates/sui-tls/src/lib.rs b/crates/sui-tls/src/lib.rs index 91a6cb666b267..7b3b9c23f5796 100644 --- a/crates/sui-tls/src/lib.rs +++ b/crates/sui-tls/src/lib.rs @@ -10,14 +10,16 @@ pub const SUI_VALIDATOR_SERVER_NAME: &str = "sui"; pub use acceptor::{TlsAcceptor, TlsConnectionInfo}; pub use certgen::SelfSignedCertificate; pub use verifier::{ - public_key_from_certificate, AllowAll, Allower, ClientCertVerifier, HashSetAllow, - ServerCertVerifier, ValidatorAllowlist, + public_key_from_certificate, AllowAll, AllowPublicKeys, Allower, ClientCertVerifier, + ServerCertVerifier, }; pub use rustls; #[cfg(test)] mod tests { + use std::collections::BTreeSet; + use super::*; use fastcrypto::ed25519::Ed25519KeyPair; use fastcrypto::traits::KeyPair; @@ -100,23 +102,16 @@ mod tests { let allowed = Ed25519KeyPair::generate(&mut rng); let disallowed = Ed25519KeyPair::generate(&mut rng); - let allowed_public_key = allowed.public().to_owned(); + let allowed_public_keys = BTreeSet::from([allowed.public().to_owned()]); let allowed_cert = SelfSignedCertificate::new(allowed.private(), SUI_VALIDATOR_SERVER_NAME); let disallowed_cert = SelfSignedCertificate::new(disallowed.private(), SUI_VALIDATOR_SERVER_NAME); - let mut allowlist = HashSetAllow::new(); + let allowlist = AllowPublicKeys::new(allowed_public_keys); let verifier = ClientCertVerifier::new(allowlist.clone(), SUI_VALIDATOR_SERVER_NAME.to_string()); - // Add our public key to the allower - allowlist - .inner_mut() - .write() - .unwrap() - .insert(allowed_public_key); - // The allowed cert passes validation verifier .verify_client_cert(&allowed_cert.rustls_certificate(), &[], UnixTime::now()) @@ -132,7 +127,7 @@ mod tests { ); // After removing the allowed public key from the set it now fails validation - allowlist.inner_mut().write().unwrap().clear(); + allowlist.update(BTreeSet::new()); let err = verifier .verify_client_cert(&allowed_cert.rustls_certificate(), &[], UnixTime::now()) .unwrap_err(); @@ -149,17 +144,10 @@ mod tests { let public_key = keypair.public().to_owned(); let cert = SelfSignedCertificate::new(keypair.private(), "not-sui"); - let mut allowlist = HashSetAllow::new(); + let allowlist = AllowPublicKeys::new(BTreeSet::from([public_key.clone()])); let client_verifier = ClientCertVerifier::new(allowlist.clone(), SUI_VALIDATOR_SERVER_NAME.to_string()); - // Add our public key to the allower - allowlist - .inner_mut() - .write() - .unwrap() - .insert(public_key.clone()); - // Allowed public key but the server-name in the cert is not the required "sui" let err = client_verifier .verify_client_cert(&cert.rustls_certificate(), &[], UnixTime::now()) @@ -210,7 +198,7 @@ mod tests { .build() .unwrap(); - let mut allowlist = HashSetAllow::new(); + let allowlist = AllowPublicKeys::new(BTreeSet::new()); let tls_config = ClientCertVerifier::new(allowlist.clone(), SUI_VALIDATOR_SERVER_NAME.to_string()) .rustls_server_config( @@ -240,11 +228,7 @@ mod tests { client.get(&server_url).send().await.unwrap_err(); // Insert the client's public key into the allowlist and verify the request is successful - allowlist - .inner_mut() - .write() - .unwrap() - .insert(client_public_key.clone()); + allowlist.update(BTreeSet::from([client_public_key.clone()])); let res = client.get(&server_url).send().await.unwrap(); let body = res.text().await.unwrap(); diff --git a/crates/sui-tls/src/verifier.rs b/crates/sui-tls/src/verifier.rs index b2dff221ffd7c..562cc34d48973 100644 --- a/crates/sui-tls/src/verifier.rs +++ b/crates/sui-tls/src/verifier.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use arc_swap::ArcSwap; use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; use rustls::crypto::WebPkiSupportedAlgorithms; @@ -10,10 +11,8 @@ use rustls::pki_types::ServerName; use rustls::pki_types::SignatureVerificationAlgorithm; use rustls::pki_types::TrustAnchor; use rustls::pki_types::UnixTime; -use std::{ - collections::HashSet, - sync::{Arc, RwLock}, -}; +use std::collections::BTreeSet; +use std::sync::Arc; static SUPPORTED_SIG_ALGS: &[&dyn SignatureVerificationAlgorithm] = &[webpki::ring::ED25519]; @@ -22,13 +21,12 @@ static SUPPORTED_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorith mapping: &[(rustls::SignatureScheme::ED25519, SUPPORTED_SIG_ALGS)], }; -pub type ValidatorAllowlist = Arc>>; - /// The Allower trait provides an interface for callers to inject decsions whether /// to allow a cert to be verified or not. This does not prform actual cert validation /// it only acts as a gatekeeper to decide if we should even try. For example, we may want /// to filter our actions to well known public keys. pub trait Allower: std::fmt::Debug + Send + Sync { + // TODO: change allower interface to use raw key bytes. fn allowed(&self, key: &Ed25519PublicKey) -> bool; } @@ -42,32 +40,28 @@ impl Allower for AllowAll { } } -/// HashSetAllow restricts keys to those that are found in the member set. non-members will not be -/// allowed. +/// AllowPublicKeys restricts keys to those that are found in the member set. non-members will +/// not be allowed. #[derive(Debug, Clone, Default)] -pub struct HashSetAllow { - inner: ValidatorAllowlist, +pub struct AllowPublicKeys { + inner: Arc>>, } -impl HashSetAllow { - pub fn new() -> Self { - let inner = Arc::new(RwLock::new(HashSet::new())); - Self { inner } - } - /// Get a reference to the inner service - pub fn inner(&self) -> &ValidatorAllowlist { - &self.inner +impl AllowPublicKeys { + pub fn new(allowed: BTreeSet) -> Self { + Self { + inner: Arc::new(ArcSwap::from_pointee(allowed)), + } } - /// Get a mutable reference to the inner service - pub fn inner_mut(&mut self) -> &mut ValidatorAllowlist { - &mut self.inner + pub fn update(&self, new_allowed: BTreeSet) { + self.inner.store(Arc::new(new_allowed)); } } -impl Allower for HashSetAllow { +impl Allower for AllowPublicKeys { fn allowed(&self, key: &Ed25519PublicKey) -> bool { - self.inner.read().unwrap().contains(key) + self.inner.load().contains(key) } } diff --git a/crates/sui-transaction-checks/src/deny.rs b/crates/sui-transaction-checks/src/deny.rs index fd10c19489c07..d0b2b9247f7c2 100644 --- a/crates/sui-transaction-checks/src/deny.rs +++ b/crates/sui-transaction-checks/src/deny.rs @@ -81,7 +81,7 @@ fn check_disabled_features( deny_if_true!( filter_config.zklogin_disabled_providers().contains( &OIDCProvider::from_iss(z.get_iss()) - .map_err(|_| SuiError::UnexpectedMessage)? + .map_err(|_| SuiError::UnexpectedMessage(z.get_iss().to_string()))? .to_string() ), "zkLogin OAuth provider is temporarily disabled" diff --git a/crates/sui-types/src/error.rs b/crates/sui-types/src/error.rs index afa128dcf7bcf..7d97c8ffa7509 100644 --- a/crates/sui-types/src/error.rs +++ b/crates/sui-types/src/error.rs @@ -443,8 +443,8 @@ pub enum SuiError { #[error("Invalid DKG message size")] InvalidDkgMessageSize, - #[error("Unexpected message.")] - UnexpectedMessage, + #[error("Unexpected message: {0}")] + UnexpectedMessage(String), // Move module publishing related errors #[error("Failed to verify the Move module, reason: {error:?}.")] @@ -809,6 +809,7 @@ impl SuiError { SuiError::ValidatorHaltedAtEpochEnd => true, SuiError::MissingCommitteeAtEpoch(..) => true, SuiError::WrongEpoch { .. } => true, + SuiError::EpochEnded(..) => true, SuiError::UserInputError { error } => { match error { diff --git a/crates/sui-types/src/transaction_executor.rs b/crates/sui-types/src/transaction_executor.rs index 8fd2ebc742a28..308c3258dafe7 100644 --- a/crates/sui-types/src/transaction_executor.rs +++ b/crates/sui-types/src/transaction_executor.rs @@ -1,9 +1,17 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::collections::BTreeMap; + +use crate::base_types::ObjectID; +use crate::effects::TransactionEffects; +use crate::effects::TransactionEvents; +use crate::error::SuiError; +use crate::object::Object; use crate::quorum_driver_types::ExecuteTransactionRequestV3; use crate::quorum_driver_types::ExecuteTransactionResponseV3; use crate::quorum_driver_types::QuorumDriverError; +use crate::transaction::TransactionData; /// Trait to define the interface for how the REST service interacts with a a QuorumDriver or a /// simulated transaction executor. @@ -14,4 +22,17 @@ pub trait TransactionExecutor: Send + Sync { request: ExecuteTransactionRequestV3, client_addr: Option, ) -> Result; + + fn simulate_transaction( + &self, + transaction: TransactionData, + ) -> Result; +} + +pub struct SimulateTransactionResult { + pub effects: TransactionEffects, + pub events: Option, + pub input_objects: BTreeMap, + pub output_objects: BTreeMap, + pub mock_gas_id: Option, } diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_name_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_name_change.snap index b86118f72d405..e4c5575f7fdce 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_name_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_name_change.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_value_changed.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_value_changed.snap index ce6f70ed769a9..0f15dec74c2f3 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_value_changed.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__constant_value_changed.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_entry_changed.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_entry_changed.snap index d51e97b52ad06..388c974ae41d5 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_entry_changed.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_entry_changed.snap @@ -3,25 +3,7 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: true -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true @@ -30,7 +12,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: true @@ -39,7 +21,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: true @@ -48,7 +30,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_function_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_function_change.snap index bfcc468e0ee46..e13049fda2aa6 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_function_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__friend_function_change.snap @@ -3,25 +3,16 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -0000000000000000000000000000000000000000000000000000000000000000::friend_module: base->upgrade: true upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false 0000000000000000000000000000000000000000000000000000000000000000::friend_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true @@ -30,7 +21,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true @@ -39,16 +30,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check.snap index e63e0028d0b00..3534c1c5410a4 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_alpha_rename.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_alpha_rename.snap index e63e0028d0b00..af0b45fe55e07 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_alpha_rename.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_alpha_rename.snap @@ -1,39 +1,28 @@ --- source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs +assertion_line: 119 expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_local_shuffle.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_local_shuffle.snap index 062f7c1443a57..79ff641479f6f 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_local_shuffle.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_equality_check_local_shuffle.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_inclusion_check.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_inclusion_check.snap index 3e910d83564fc..f9c0fa5f6dc7d 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_inclusion_check.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_inclusion_check.snap @@ -1,39 +1,28 @@ --- source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs +assertion_line: 119 expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_invalid_equality_inclusion_check.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_invalid_equality_inclusion_check.snap index 062f7c1443a57..79ff641479f6f 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_invalid_equality_inclusion_check.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__large_package_invalid_equality_inclusion_check.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::ascii: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::ascii: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_and_friend_changes.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_and_friend_changes.snap index c6681aa5ba95a..bfb2725c8427f 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_and_friend_changes.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_and_friend_changes.snap @@ -3,25 +3,7 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true @@ -30,7 +12,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: true @@ -39,16 +21,16 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false - upgrade->base: false + upgrade->base: true 0000000000000000000000000000000000000000000000000000000000000000::friend_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_fun_entry_removed.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_fun_entry_removed.snap index d51e97b52ad06..388c974ae41d5 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_fun_entry_removed.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__private_entry_fun_entry_removed.snap @@ -3,25 +3,7 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: true -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -0000000000000000000000000000000000000000000000000000000000000000::friend_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true @@ -30,7 +12,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: true @@ -39,7 +21,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: true @@ -48,7 +30,7 @@ Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_la upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_alpha_rename.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_alpha_rename.snap index b86118f72d405..e4c5575f7fdce 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_alpha_rename.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_alpha_rename.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_permute.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_permute.snap index ce6f70ed769a9..0f15dec74c2f3 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_permute.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_param_permute.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_rename.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_rename.snap index 09bbea5822c3a..ff77031427299 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_rename.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__public_fun_rename.snap @@ -3,40 +3,28 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true ======== Equal 0000000000000000000000000000000000000000000000000000000000000000::base_module: diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_name_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_name_change.snap index 09bbea5822c3a..0b4ea50756164 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_name_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_name_change.snap @@ -1,39 +1,28 @@ --- source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs +assertion_line: 119 expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder.snap index 09bbea5822c3a..0b903a6a27ef1 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder_no_name_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder_no_name_change.snap index 09bbea5822c3a..0b903a6a27ef1 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder_no_name_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_field_reorder_no_name_change.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_layout_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_layout_change.snap index 09bbea5822c3a..0b903a6a27ef1 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_layout_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_layout_change.snap @@ -3,37 +3,25 @@ source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: true upgrade->base: true diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_name_change.snap b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_name_change.snap index 09bbea5822c3a..50a9c5c431e77 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_name_change.snap +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/snapshots/tests__struct_name_change.snap @@ -1,42 +1,31 @@ --- source: crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs +assertion_line: 119 expression: results --- ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: true, check_private_entry_linking: true, disallowed_new_abilities: [Key, ] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false ==== ==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } +Compatibility { check_datatype_layout: false, check_private_entry_linking: false, disallowed_new_abilities: [] } 0000000000000000000000000000000000000000000000000000000000000000::base_module: base->upgrade: false upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: [Copy, Drop, Store, Key, ], disallow_change_datatype_type_params: true, disallow_new_variants: true } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: false - upgrade->base: false -==== -==== -Compatibility { check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: [], disallow_change_datatype_type_params: false, disallow_new_variants: false } -0000000000000000000000000000000000000000000000000000000000000000::base_module: - base->upgrade: true - upgrade->base: true ======== Equal 0000000000000000000000000000000000000000000000000000000000000000::base_module: diff --git a/crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs b/crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs index 4f491e8f3cc09..ce1f104d25942 100644 --- a/crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs +++ b/crates/sui-upgrade-compatibility-transactional-tests/tests/tests.rs @@ -5,7 +5,6 @@ use std::path::{Path, PathBuf}; use move_binary_format::{ compatibility::{Compatibility, InclusionCheck}, - file_format::AbilitySet, normalized, CompiledModule, }; use sui_move_build::{BuildConfig, SuiPackageHooks}; @@ -52,47 +51,12 @@ fn check_all_compatibilities( assert_eq!(base.len(), upgraded.len()); let compatibility_types = [ + // Full compat skip check private entry linking + Compatibility::upgrade_check(), + // Full compat but allow any new abilities Compatibility::full_check(), - // Full compat but allow private entry functions to change - Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: true, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }, - // Full compat but allow private entry functions and friends to change - Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }, - // Full compat but allow friends to change - Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - check_private_entry_linking: true, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }, - // Full compat but allow new enum variants to be added - Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: true, - check_private_entry_linking: true, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }, + // Full compat only disallow new key ability + Compatibility::framework_upgrade_check(), Compatibility::no_check(), ]; diff --git a/crates/sui/src/upgrade_compatibility.rs b/crates/sui/src/upgrade_compatibility.rs index d2a9eaf39813a..bad3fca2c1a5d 100644 --- a/crates/sui/src/upgrade_compatibility.rs +++ b/crates/sui/src/upgrade_compatibility.rs @@ -6,8 +6,7 @@ mod upgrade_compatibility_tests; use anyhow::{anyhow, Context, Error}; -use std::collections::{BTreeSet, HashMap}; -use thiserror::Error; +use std::collections::HashMap; use move_binary_format::{ compatibility::Compatibility, @@ -19,7 +18,6 @@ use move_binary_format::{ use move_core_types::{ account_address::AccountAddress, identifier::{IdentStr, Identifier}, - language_storage::ModuleId, }; use sui_json_rpc_types::{SuiObjectDataOptions, SuiRawData}; use sui_protocol_config::ProtocolConfig; @@ -83,10 +81,6 @@ pub(crate) enum UpgradeCompatibilityModeError { name: Identifier, old_function: Function, }, - FunctionMissingFriend { - name: Identifier, - old_function: Function, - }, FunctionMissingEntry { name: Identifier, old_function: Function, @@ -100,16 +94,11 @@ pub(crate) enum UpgradeCompatibilityModeError { name: Identifier, old_function: Function, }, - FunctionLostFriendVisibility { - name: Identifier, - old_function: Function, - }, FunctionEntryCompatibility { name: Identifier, old_function: Function, new_function: Function, }, - FriendModuleMissing(BTreeSet, BTreeSet), } impl UpgradeCompatibilityModeError { @@ -120,9 +109,7 @@ impl UpgradeCompatibilityModeError { | UpgradeCompatibilityModeError::EnumAbilityMismatch { .. } | UpgradeCompatibilityModeError::EnumTypeParamMismatch { .. } | UpgradeCompatibilityModeError::FunctionMissingPublic { .. } - | UpgradeCompatibilityModeError::FunctionLostPublicVisibility { .. } => { - compatability.check_datatype_and_pub_function_linking - } + | UpgradeCompatibilityModeError::FunctionLostPublicVisibility { .. } => true, UpgradeCompatibilityModeError::StructFieldMismatch { .. } | UpgradeCompatibilityModeError::EnumVariantMissing { .. } @@ -131,16 +118,11 @@ impl UpgradeCompatibilityModeError { } UpgradeCompatibilityModeError::StructMissing { .. } - | UpgradeCompatibilityModeError::EnumMissing { .. } => { - compatability.check_datatype_and_pub_function_linking - || compatability.check_datatype_layout - } + | UpgradeCompatibilityModeError::EnumMissing { .. } => true, UpgradeCompatibilityModeError::FunctionSignatureMismatch { old_function, .. } => { if old_function.visibility == Visibility::Public { - return compatability.check_datatype_and_pub_function_linking; - } else if old_function.visibility == Visibility::Friend { - return compatability.check_friend_linking; + return true; } if old_function.is_entry { compatability.check_private_entry_linking @@ -149,18 +131,12 @@ impl UpgradeCompatibilityModeError { } } - UpgradeCompatibilityModeError::FunctionMissingFriend { .. } - | UpgradeCompatibilityModeError::FunctionLostFriendVisibility { .. } - | UpgradeCompatibilityModeError::FriendModuleMissing(_, _) => { - compatability.check_friend_linking - } - UpgradeCompatibilityModeError::FunctionMissingEntry { .. } | UpgradeCompatibilityModeError::FunctionEntryCompatibility { .. } => { compatability.check_private_entry_linking } UpgradeCompatibilityModeError::EnumNewVariant { .. } => { - compatability.disallow_new_variants + compatability.check_datatype_layout } } } @@ -302,14 +278,6 @@ impl CompatibilityMode for CliCompatibilityMode { }); } - fn function_missing_friend(&mut self, name: &Identifier, old_function: &Function) { - self.errors - .push(UpgradeCompatibilityModeError::FunctionMissingFriend { - name: name.clone(), - old_function: old_function.clone(), - }); - } - fn function_missing_entry(&mut self, name: &Identifier, old_function: &Function) { self.errors .push(UpgradeCompatibilityModeError::FunctionMissingEntry { @@ -341,15 +309,6 @@ impl CompatibilityMode for CliCompatibilityMode { ); } - fn function_lost_friend_visibility(&mut self, name: &Identifier, old_function: &Function) { - self.errors.push( - UpgradeCompatibilityModeError::FunctionLostFriendVisibility { - name: name.clone(), - old_function: old_function.clone(), - }, - ); - } - fn function_entry_compatibility( &mut self, name: &Identifier, @@ -364,18 +323,6 @@ impl CompatibilityMode for CliCompatibilityMode { }); } - fn friend_module_missing( - &mut self, - old_modules: BTreeSet, - new_modules: BTreeSet, - ) { - self.errors - .push(UpgradeCompatibilityModeError::FriendModuleMissing( - old_modules.clone(), - new_modules.clone(), - )); - } - fn finish(&self, compatability: &Compatibility) -> Result<(), Self::Error> { let errors: Vec = self .errors diff --git a/crates/suins-indexer/src/main.rs b/crates/suins-indexer/src/main.rs index a42c0e327811f..0f3db957205e2 100644 --- a/crates/suins-indexer/src/main.rs +++ b/crates/suins-indexer/src/main.rs @@ -107,6 +107,7 @@ impl SuinsIndexerWorker { #[async_trait] impl Worker for SuinsIndexerWorker { + type Result = (); async fn process_checkpoint(&self, checkpoint: &CheckpointData) -> Result<()> { let checkpoint_seq_number = checkpoint.checkpoint_summary.sequence_number; let (updates, removals) = self.indexer.process_checkpoint(checkpoint); diff --git a/crates/suiop-cli/src/cli/ci/image.rs b/crates/suiop-cli/src/cli/ci/image.rs index 9486cbfaf4358..a2c1e56563957 100644 --- a/crates/suiop-cli/src/cli/ci/image.rs +++ b/crates/suiop-cli/src/cli/ci/image.rs @@ -90,7 +90,7 @@ pub enum ImageAction { /// Optional repo region, default to "us-central1" #[arg(long)] repo_region: Option, - /// Optional image name, default to "app", only used if multiple images are built within one repo + /// Optional image tags, default to "" #[arg(long)] image_tag: Option, /// Optional image name, default to "app", only used if multiple images are built within one repo @@ -117,6 +117,12 @@ pub enum ImageAction { /// Optional build args to pass to the docker build command #[arg(long)] build_args: Vec, + /// Optional flag to force build even if build pod already exists + #[arg(short = 'f', long)] + force: bool, + /// Optional flag to target the image, used for multi-stage builds + #[arg(short = 't', long)] + image_target: Option, }, #[command(name = "query")] Query { @@ -160,6 +166,8 @@ struct RequestBuildRequest { memory: String, disk: String, build_args: Vec, + force: bool, + image_target: Option, } #[derive(serde::Serialize)] @@ -167,7 +175,6 @@ struct QueryBuildsRequest { repo_name: String, limit: u32, } - #[derive(serde::Serialize)] struct ImageStatusRequest { repo_name: String, @@ -303,6 +310,8 @@ async fn send_image_request(token: &str, action: &ImageAction) -> Result<()> { memory: _, disk: _, build_args: _, + force: _, + image_target, } => { let ref_type = ref_type.clone().unwrap_or(RefType::Branch); let ref_val = ref_val.clone().unwrap_or("main".to_string()); @@ -313,6 +322,9 @@ async fn send_image_request(token: &str, action: &ImageAction) -> Result<()> { if !image_tag.is_empty() { image_info += &format!(":{}", image_tag); } + if !image_target.is_none() { + image_info += &format!("@{}", image_target.as_ref().unwrap()); + } println!( "Requested built image for repo: {}, ref: {}, dockerfile: {}, image: {}", repo_name.green(), @@ -454,6 +466,8 @@ fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::Request memory, disk, build_args, + force, + image_target, } => { let full_url = format!("{}{}", api_server, ENDPOINT); debug!("full_url: {}", full_url); @@ -500,6 +514,8 @@ fn generate_image_request(token: &str, action: &ImageAction) -> reqwest::Request memory, disk, build_args: build_args.clone(), + force: *force, + image_target: image_target.clone(), }; debug!("req body: {:?}", body); req.json(&body).headers(generate_headers_with_auth(token)) diff --git a/crates/suiop-cli/src/cli/ci/mod.rs b/crates/suiop-cli/src/cli/ci/mod.rs index ecbae85e75a19..dc8d6e5ccc5a6 100644 --- a/crates/suiop-cli/src/cli/ci/mod.rs +++ b/crates/suiop-cli/src/cli/ci/mod.rs @@ -21,7 +21,7 @@ pub(crate) enum CIAction { #[clap(aliases = ["k", "key"])] Keys(KeyArgs), #[clap(aliases = ["i"])] - Image(ImageArgs), + Image(Box), } pub async fn ci_cmd(args: &CIArgs) -> Result<()> { diff --git a/crates/suiop-cli/src/cli/pulumi/init.rs b/crates/suiop-cli/src/cli/pulumi/init.rs index 34bdeeff2d496..7fa9551722fc7 100644 --- a/crates/suiop-cli/src/cli/pulumi/init.rs +++ b/crates/suiop-cli/src/cli/pulumi/init.rs @@ -12,6 +12,7 @@ use std::fs; use std::path::{Path, PathBuf}; use tracing::{debug, error, info, warn}; +#[derive(clap::Subcommand, Clone, Debug)] pub enum ProjectType { App, Service, diff --git a/crates/suiop-cli/src/cli/pulumi/mod.rs b/crates/suiop-cli/src/cli/pulumi/mod.rs index 2da28b08f29a2..68b0d6145e999 100644 --- a/crates/suiop-cli/src/cli/pulumi/mod.rs +++ b/crates/suiop-cli/src/cli/pulumi/mod.rs @@ -8,7 +8,7 @@ use anyhow::Result; use clap::Parser; use init::ProjectType; use setup::ensure_gcloud; -use setup::ensure_setup; +use setup::ensure_pulumi_setup; #[derive(Parser, Debug, Clone)] pub struct PulumiArgs { @@ -21,20 +21,9 @@ pub enum PulumiAction { /// initialize a new pulumi project #[command(name = "init", aliases=["i"])] InitProject { - /// initialize app project - #[arg(short, long, group = "type")] - app: bool, - - #[arg(short, long, group = "type")] - service: bool, - - /// initialize barebones project (default) - #[arg(short, long, group = "type")] - basic: bool, - - /// initialize cronjob project - #[arg(short, long, group = "type")] - cronjob: bool, + /// subcommand for project type + #[command(subcommand)] + project_type: ProjectType, /// use GCP KMS as encryption provider #[arg(short, long, group = "feature")] @@ -46,26 +35,17 @@ pub enum PulumiAction { }, } -pub async fn pulumi_cmd(args: &PulumiArgs) -> Result<()> { - ensure_setup()?; +pub fn pulumi_cmd(args: &PulumiArgs) -> Result<()> { + ensure_pulumi_setup()?; match &args.action { PulumiAction::InitProject { - app, - service, - basic, - cronjob, + project_type, kms, project_name, } => { if *kms { ensure_gcloud()?; } - let project_type = match (app, service, basic, cronjob) { - (false, true, false, false) => ProjectType::Service, - (true, false, false, false) => ProjectType::App, - (false, false, false, true) => ProjectType::CronJob, - (_, _, _, _) => ProjectType::Basic, - }; project_type.create_project(kms, project_name.clone()) } } diff --git a/crates/suiop-cli/src/cli/pulumi/setup.rs b/crates/suiop-cli/src/cli/pulumi/setup.rs index c5c4452ce5194..fe4922a29ce2c 100644 --- a/crates/suiop-cli/src/cli/pulumi/setup.rs +++ b/crates/suiop-cli/src/cli/pulumi/setup.rs @@ -156,7 +156,7 @@ pub fn ensure_gcloud() -> Result<()> { } } -pub fn ensure_setup() -> Result<()> { +pub fn ensure_pulumi_setup() -> Result<()> { let home = env::var("HOME").unwrap(); // check for marker file let setup_marker = PathBuf::from(format!("{}/.suiop/pulumi_setup", home)); diff --git a/crates/suiop-cli/src/main.rs b/crates/suiop-cli/src/main.rs index f897e903feef9..3d3c7c99c83ea 100644 --- a/crates/suiop-cli/src/main.rs +++ b/crates/suiop-cli/src/main.rs @@ -68,7 +68,7 @@ async fn main() -> Result<()> { incidents_cmd(&args).await?; } Resource::Pulumi(args) => { - pulumi_cmd(&args).await?; + pulumi_cmd(&args)?; } Resource::Service(args) => { service_cmd(&args).await?; diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index 5f65067cc4525..825f375d1059b 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -62,6 +62,7 @@ const DEFAULT_DB_WAL_SIZE: usize = 1024; // Environment variable to control behavior of write throughput optimized tables. const ENV_VAR_L0_NUM_FILES_COMPACTION_TRIGGER: &str = "L0_NUM_FILES_COMPACTION_TRIGGER"; const DEFAULT_L0_NUM_FILES_COMPACTION_TRIGGER: usize = 4; +const DEFAULT_UNIVERSAL_COMPACTION_L0_NUM_FILES_COMPACTION_TRIGGER: usize = 80; const ENV_VAR_MAX_WRITE_BUFFER_SIZE_MB: &str = "MAX_WRITE_BUFFER_SIZE_MB"; const DEFAULT_MAX_WRITE_BUFFER_SIZE_MB: usize = 256; const ENV_VAR_MAX_WRITE_BUFFER_NUMBER: &str = "MAX_WRITE_BUFFER_NUMBER"; @@ -915,7 +916,7 @@ impl DBMap { property_name: &std::ffi::CStr, ) -> Result { match rocksdb.property_int_value_cf(cf, property_name) { - Ok(Some(value)) => Ok(value.try_into().unwrap()), + Ok(Some(value)) => Ok(value.min(i64::MAX as u64).try_into().unwrap_or_default()), Ok(None) => Ok(0), Err(e) => Err(TypedStoreError::RocksDBError(e.into_string())), } @@ -2443,7 +2444,7 @@ impl DBOptions { // Optimize tables with a mix of lookup and scan workloads. pub fn optimize_for_read(mut self, block_cache_size_mb: usize) -> DBOptions { self.options - .set_block_based_table_factory(&get_block_options(block_cache_size_mb)); + .set_block_based_table_factory(&get_block_options(block_cache_size_mb, 16 << 10)); self } @@ -2500,6 +2501,75 @@ impl DBOptions { self } + // Optimize tables receiving significant insertions, without any deletions. + // TODO: merge this function with optimize_for_write_throughput(), and use a flag to + // indicate if deletion is received. + pub fn optimize_for_write_throughput_no_deletion(mut self) -> DBOptions { + // Increase write buffer size to 256MiB. + let write_buffer_size = read_size_from_env(ENV_VAR_MAX_WRITE_BUFFER_SIZE_MB) + .unwrap_or(DEFAULT_MAX_WRITE_BUFFER_SIZE_MB) + * 1024 + * 1024; + self.options.set_write_buffer_size(write_buffer_size); + // Increase write buffers to keep to 6 before slowing down writes. + let max_write_buffer_number = read_size_from_env(ENV_VAR_MAX_WRITE_BUFFER_NUMBER) + .unwrap_or(DEFAULT_MAX_WRITE_BUFFER_NUMBER); + self.options + .set_max_write_buffer_number(max_write_buffer_number.try_into().unwrap()); + // Keep 1 write buffer so recent writes can be read from memory. + self.options + .set_max_write_buffer_size_to_maintain((write_buffer_size).try_into().unwrap()); + + // Switch to universal compactions. + self.options + .set_compaction_style(rocksdb::DBCompactionStyle::Universal); + self.options.set_num_levels(1); + let mut compaction_options = rocksdb::UniversalCompactOptions::default(); + compaction_options.set_max_size_amplification_percent(10000); + compaction_options.set_stop_style(rocksdb::UniversalCompactionStopStyle::Similar); + self.options + .set_universal_compaction_options(&compaction_options); + + let max_level_zero_file_num = read_size_from_env(ENV_VAR_L0_NUM_FILES_COMPACTION_TRIGGER) + .unwrap_or(DEFAULT_UNIVERSAL_COMPACTION_L0_NUM_FILES_COMPACTION_TRIGGER); + self.options.set_level_zero_file_num_compaction_trigger( + max_level_zero_file_num.try_into().unwrap(), + ); + self.options.set_level_zero_slowdown_writes_trigger( + (max_level_zero_file_num * 12).try_into().unwrap(), + ); + self.options + .set_level_zero_stop_writes_trigger((max_level_zero_file_num * 16).try_into().unwrap()); + + // Increase sst file size to 128MiB. + self.options.set_target_file_size_base( + read_size_from_env(ENV_VAR_TARGET_FILE_SIZE_BASE_MB) + .unwrap_or(DEFAULT_TARGET_FILE_SIZE_BASE_MB) as u64 + * 1024 + * 1024, + ); + + // This should be a no-op for universal compaction but increasing it to be safe. + self.options + .set_max_bytes_for_level_base((write_buffer_size * max_level_zero_file_num) as u64); + + self + } + + // Overrides the block options with different block cache size and block size. + pub fn set_block_options( + mut self, + block_cache_size_mb: usize, + block_size_bytes: usize, + ) -> DBOptions { + self.options + .set_block_based_table_factory(&get_block_options( + block_cache_size_mb, + block_size_bytes, + )); + self + } + // Disables write stalling and stopping based on pending compaction bytes. pub fn disable_write_throttling(mut self) -> DBOptions { self.options.set_soft_pending_compaction_bytes_limit(0); @@ -2524,7 +2594,6 @@ pub fn default_db_options() -> DBOptions { opt.set_table_cache_num_shard_bits(10); // LSM compression settings - opt.set_min_level_to_compress(2); opt.set_compression_type(rocksdb::DBCompressionType::Lz4); opt.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd); opt.set_bottommost_zstd_max_train_bytes(1024 * 1024, true); @@ -2552,7 +2621,9 @@ pub fn default_db_options() -> DBOptions { opt.set_enable_pipelined_write(true); - opt.set_block_based_table_factory(&get_block_options(128)); + // Increase block size to 16KiB. + // https://github.com/EighteenZi/rocksdb_wiki/blob/master/Memory-usage-in-RocksDB.md#indexes-and-filter-blocks + opt.set_block_based_table_factory(&get_block_options(128, 16 << 10)); // Set memtable bloomfilter. opt.set_memtable_prefix_bloom_ratio(0.02); @@ -2563,15 +2634,14 @@ pub fn default_db_options() -> DBOptions { } } -fn get_block_options(block_cache_size_mb: usize) -> BlockBasedOptions { +fn get_block_options(block_cache_size_mb: usize, block_size_bytes: usize) -> BlockBasedOptions { // Set options mostly similar to those used in optimize_for_point_lookup(), // except non-default binary and hash index, to hopefully reduce lookup latencies // without causing any regression for scanning, with slightly more memory usages. // https://github.com/facebook/rocksdb/blob/11cb6af6e5009c51794641905ca40ce5beec7fee/options/options.cc#L611-L621 let mut block_options = BlockBasedOptions::default(); - // Increase block size to 16KiB. - // https://github.com/EighteenZi/rocksdb_wiki/blob/master/Memory-usage-in-RocksDB.md#indexes-and-filter-blocks - block_options.set_block_size(16 * 1024); + // Overrides block size. + block_options.set_block_size(block_size_bytes); // Configure a block cache. block_options.set_block_cache(&Cache::new_lru_cache(block_cache_size_mb << 20)); // Set a bloomfilter with 1% false positive rate. diff --git a/docker/sui-graphql-rpc-staging/Dockerfile b/docker/sui-graphql-rpc-staging/Dockerfile new file mode 100644 index 0000000000000..a3f9bfb0fd571 --- /dev/null +++ b/docker/sui-graphql-rpc-staging/Dockerfile @@ -0,0 +1,33 @@ +# Build application +# +# Copy in all crates, Cargo.toml and Cargo.lock unmodified, +# and build the application. +FROM rust:1.81-bullseye AS builder +ARG PROFILE=release +ENV PROFILE=$PROFILE +ARG GIT_REVISION +ENV GIT_REVISION=$GIT_REVISION +WORKDIR "$WORKDIR/sui" +RUN apt-get update && apt-get install -y cmake clang libpq-dev + +COPY Cargo.toml Cargo.lock ./ +COPY consensus consensus +COPY crates crates +COPY sui-execution sui-execution +COPY narwhal narwhal +COPY external-crates external-crates + +RUN cargo build --profile ${PROFILE} --bin sui-graphql-rpc --features staging + +# Production Image +FROM debian:bullseye-slim AS runtime +WORKDIR "$WORKDIR/sui" +# Both bench and release profiles copy from release dir +RUN mkdir -p /opt/sui/bin +COPY --from=builder /sui/target/release/sui-graphql-rpc /opt/sui/bin +RUN apt-get update && apt-get install -y libpq5 ca-certificates libpq-dev postgresql + +ARG BUILD_DATE +ARG GIT_REVISION +LABEL build-date=$BUILD_DATE +LABEL git-revision=$GIT_REVISION diff --git a/docker/sui-graphql-rpc-staging/build.sh b/docker/sui-graphql-rpc-staging/build.sh new file mode 100755 index 0000000000000..84038b6538c15 --- /dev/null +++ b/docker/sui-graphql-rpc-staging/build.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Copyright (c) Mysten Labs, Inc. +# SPDX-License-Identifier: Apache-2.0 + +# fast fail. +set -e + +DIR="$( cd "$( dirname "$0" )" && pwd )" +REPO_ROOT="$(git rev-parse --show-toplevel)" +DOCKERFILE="$DIR/Dockerfile" +GIT_REVISION="$(git describe --always --abbrev=12 --dirty --exclude '*')" +BUILD_DATE="$(date -u +'%Y-%m-%d')" + +# option to build using debug symbols +if [ "$1" = "--debug-symbols" ]; then + PROFILE="bench" + echo "Building with full debug info enabled ... WARNING: binary size might significantly increase" + shift +else + PROFILE="release" +fi + +echo +echo "Building sui-graphql-rpc docker image" +echo "Dockerfile: \t$DOCKERFILE" +echo "docker context: $REPO_ROOT" +echo "build date: \t$BUILD_DATE" +echo "git revision: \t$GIT_REVISION" +echo + +docker build -f "$DOCKERFILE" "$REPO_ROOT" \ + --build-arg GIT_REVISION="$GIT_REVISION" \ + --build-arg BUILD_DATE="$BUILD_DATE" \ + --build-arg PROFILE="$PROFILE" \ + --features staging \ + "$@" diff --git a/docs/content/guides.mdx b/docs/content/guides.mdx index 717410cc223e5..445cecf8a911f 100644 --- a/docs/content/guides.mdx +++ b/docs/content/guides.mdx @@ -36,23 +36,32 @@ Learn the basics of Sui and how they might differ from other blockchains. Transactions on Sui are more powerful than other blockchains. Learn why and how to use them. - - + Monitor the Sui network and programmatically react to on-chain events. +## Coins, tokens, and NFTs + +Learn how to mint various tokens on the Sui blockchain. + + + + + + ## Validating and operating nodes on Sui Processes and guides for validators and node operators on the Sui network. - - + Learn how to operate a Full node on Sui. Optimize your Full node configuration for efficient node operation. + + diff --git a/docs/content/guides/developer.mdx b/docs/content/guides/developer.mdx index 98bfd3ca82af5..a21919b55d268 100644 --- a/docs/content/guides/developer.mdx +++ b/docs/content/guides/developer.mdx @@ -23,6 +23,15 @@ The Sui 101 section introduces the basics of Sui that help you create smart cont Go to [Sui 101](./developer/sui-101.mdx). +## Coins, tokens, and NFTs + +Minting coins, tokens, and NFTs on Sui provides the opportunity to build communities on the Sui network around these objects or to promote and support off-chain businesses. Refer to the appropriate guide to get started. + +Go to +- [Coins and Tokens](./developer/coin.mdx) +- [NFTs](./developer/nft.mdx) +- [Stablecoins](./developer/stablecoins.mdx) + ## Cryptography The Cryptography section demonstrates how to secure your smart contracts with cryptography to ensure authentication for access to sensitive data. diff --git a/docs/content/guides/developer/coin.mdx b/docs/content/guides/developer/coin.mdx index 6f695e506ecc7..1157cf8861a7c 100644 --- a/docs/content/guides/developer/coin.mdx +++ b/docs/content/guides/developer/coin.mdx @@ -1,5 +1,6 @@ --- title: Create Coins and Tokens +description: Learn how to mint coins and tokens on the Sui network. --- Coins and tokens on Sui are similar. In practice, the terms are used interchangeably, but there are some differences in their implementation. You can learn about these differences in the respective standard documentation, [Closed-Loop Token](../../standards/closed-loop-token.mdx) and [Coin](../../standards/coin.mdx). @@ -68,4 +69,5 @@ See [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard for comp - [Regulated Coin and Deny List](./coin/regulated.mdx): Create a regulated coin and add or remove names from the deny list. - [Loyalty Token](./coin/loyalty.mdx): Create a token to reward user loyalty. - [In-Game Token](./coin/in-game-token.mdx): Create tokens that can be used only within a mobile game. +- [Stablecoins](./stablecoins): The Sui network has native stablecoins, including USDC. - [One Time Witness](https://move-book.com/programmability/one-time-witness.html): The Move Book documentation of the one time witness pattern. diff --git a/docs/content/guides/developer/getting-started/graphql-rpc.mdx b/docs/content/guides/developer/getting-started/graphql-rpc.mdx index 93f1b805765bd..428d141db5cf8 100644 --- a/docs/content/guides/developer/getting-started/graphql-rpc.mdx +++ b/docs/content/guides/developer/getting-started/graphql-rpc.mdx @@ -161,7 +161,7 @@ This example finds the balance changes of all the transactions where a given add query ($address: SuiAddress!) { transactionBlocks(filter: { function: "0x3::sui_system::request_add_stake" - signAddress: $address + sentAddress: $address }) { nodes { digest @@ -370,7 +370,7 @@ Examples in the repository are designed to work with the version of GraphQL buil ## Related links - [GraphQL migration](../advanced/graphql-migration.mdx): Migrating to GraphQL guides you through migrating Sui RPC projects from JSON-RPC to GraphQL. -- [GraphQL concepts](../../../concepts/graphql-rpc.mdx): GraphQL for Sui RPC examines the elements of GraphQL that you should know to get the most from the service. +- [GraphQL concepts](../../../concepts/graphql-rpc.mdx): GraphQL for Sui RPC examines the elements of GraphQL that you should know to get the most from the service. - [GraphQL reference](../../../references/sui-graphql.mdx): Auto-generated GraphQL reference for Sui RPC. -- [Sui Testnet GraphiQL](https://sui-testnet.mystenlabs.com/graphql): Sui GraphiQL IDE for the Testnet network. +- [Sui Testnet GraphiQL](https://sui-testnet.mystenlabs.com/graphql): Sui GraphiQL IDE for the Testnet network. - [Sui Mainnet GraphiQL](https://sui-mainnet.mystenlabs.com/graphql): Sui GraphiQL IDE for the Mainnet network. diff --git a/docs/content/guides/developer/images/stablecoinsuccess.png b/docs/content/guides/developer/images/stablecoinsuccess.png new file mode 100644 index 0000000000000..2c8262ffb9516 Binary files /dev/null and b/docs/content/guides/developer/images/stablecoinsuccess.png differ diff --git a/docs/content/guides/developer/images/stablecoinui.png b/docs/content/guides/developer/images/stablecoinui.png new file mode 100644 index 0000000000000..6e4887505f200 Binary files /dev/null and b/docs/content/guides/developer/images/stablecoinui.png differ diff --git a/docs/content/guides/developer/nft.mdx b/docs/content/guides/developer/nft.mdx index 414909353d0e6..451574e4f13f6 100644 --- a/docs/content/guides/developer/nft.mdx +++ b/docs/content/guides/developer/nft.mdx @@ -1,6 +1,7 @@ --- title: Create a Non-Fungible Token keywords: [ERC-721, NFT] +description: On Sui, everything is an object. Moreover, everything is a non-fungible token (NFT) as its objects are unique, non-fungible, and owned. --- On Sui, everything is an object. Moreover, everything is a non-fungible token (NFT) as its objects are unique, non-fungible, and owned. diff --git a/docs/content/guides/developer/stablecoins.mdx b/docs/content/guides/developer/stablecoins.mdx new file mode 100644 index 0000000000000..2457d8d872c43 --- /dev/null +++ b/docs/content/guides/developer/stablecoins.mdx @@ -0,0 +1,234 @@ +--- +title: Stablecoins on Sui +sidebar_label: Stablecoins +description: Stablecoins are a type of cryptocurrency that are designed to maintain a stable value relative to a fiat currency or a basket of assets. +--- + +Stablecoins are a type of cryptocurrency that are designed to maintain a stable value relative to a fiat currency or a basket of assets. They are widely used for trading, lending, and as a store of value. + +## Available stablecoins + +On Sui, you can interact with various stablecoins such as USDC, USDT, Agora, and Ondo USDY. + +### USDC (USD Coin) + +USDC is a fully collateralized US dollar stablecoin issued by regulated financial institutions. Each USDC token is backed by one US dollar held in reserve. USDC is widely used for trading, payments, and as a stable store of value. + +For more detailed information on how to interact with USDC on Sui, refer to the [USDC guide](#usdc-guide). + +**Site:** [Circle](https://www.circle.com/en/usdc) + +### USDT (Tether) + +USDT, also known as Tether, is one of the oldest and most widely used stablecoins. It is pegged to the US dollar and is backed by a mix of reserves, including cash, cash equivalents, and other assets. + +USDT is currently not issued natively on Sui. For more information on bridging USDT to Sui, refer to [SUI Bridging](../../concepts/tokenomics/sui-bridging.mdx). + +**Site:** [Tether](https://tether.to/) + +### Agora + +AUSD is a fully collateralized US dollar stablecoin issued by Agora Finance. + +**Site:** [Agora Finance](https://www.agora.finance/) + +### Ondo USDY +USDY is a fully collateralized US dollar stablecoin issued by Ondo Finance, allowing users to earn yield from US Treasury Bills. + +**Site:** [Ondo Finance](https://ondo.finance/) + +## How to transfer USDC on Sui {#usdc-guide} + +USDC provides the ability to transfer dollars over public blockchains using smart contracts. The smart contract allows users to send, receive, and store dollars on chain with a wallet. + +Here's a quickstart guide for developers to build an app to perform their first USDC transfer on Sui. + +### Contract addresses + +- [Testnet](https://suiscan.xyz/testnet/coin/0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC) + ``` + 0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC + ``` +- [Mainnet](https://suiscan.xyz/coin/0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC) + ``` + 0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC + ``` + +### Prerequisites + +Before you start building the sample app to perform a USDC transfer, ensure you meet the following prerequisites: + +- [Node.js](https://nodejs.org/en) and [npm](https://www.npmjs.com/): Ensure that you have Node.js and npm installed on your machine. You can download and install Node.js from [nodejs.org](http://nodejs.org). npm comes with Node.js. +- [Sui Wallet](https://chromewebstore.google.com/detail/sui-wallet/opcgpfmipidbgpenhmajoajpbobppdil): Install the Sui Wallet browser extension, set up your wallet, and connect it to the appropriate network. Ensure that your wallet is funded with: + - Some SUI tokens to cover transaction fees. (**Request Testnet SUI Tokens** from the **Wallet Settings** page) + - USDC tokens for the transfer. ([USDC Testnet Faucet](https://faucet.circle.com/)) + +### Installation + +To begin, create a new project directory (`usdc-transfer-app`, for example) and initialize it with pnpm: + +```bash +pnpm init +``` + +To keep a small footprint, the instruction uses [Parcel](https://parceljs.org/) to render the example React-based app. You can use a more robust framework, like Next.js, or a different package manager, but you must modify the steps appropriately. + +```bash +pnpm add react react-dom parcel +``` + +Install Sui dependencies next: + +```bash +pnpm add @mysten/dapp-kit @mysten/sui @tanstack/react-query +``` + +Finally, update your `package.json` to include the following script: + +```json +"scripts": { + "start": "parcel index.html" + }, +``` + +### Create site structure + +At the root of your project directory, create an `index.html` file and save the following content to it: + +
+ +`index.html` file + +{@inject: examples/usdc-transfer-app/index.html} +
+ +Next, create and save an `index.js` file in the same location with the following code: + +
+ +`index.js` file + +{@inject: examples/usdc-transfer-app/index.js} +
+ +To style the output for the site, create a `global.css` file in the root of your project. Copy and paste the following minimal styling into the file and save: + +
+ +`global.css` file + +{@inject: examples/usdc-transfer-app/global.css} +
+ +Finally, create an `App.js` file at the project root. When complete, this file will hold the logic for the USDC transfer. For now, just create the file to display some basic text. This example does not use TypeScript to keep the instruction focused, but you can certainly use it in your own projects. + +
+ +`App.js` file + +{@inject: examples/usdc-transfer-app/lib/App-stub.js noComments noTitle} +
+ +Start the development server to make sure the default app runs using the following command in the root of your project: + +```bash +pnpm start +``` + +Open a browser to `localhost:1234` to make sure the Hello World text appears. + +### Import code and setup + +Open the `App.js` file and make the changes that follow. + +First, import necessary libraries and set up the network configuration. + +{@inject: examples/usdc-transfer-app/App.js#setup} + +This component handles the main functionality of the application, including wallet connection, defining token, and token transfer. + +#### Define USDC Testnet token contract + +{@inject: examples/usdc-transfer-app/App.js#variable=USDC_TYPE} + +#### State management + +Create a `HomeContent()` function that is going to be responsible for setting up and displaying the UI. At the top of this function, define state variables to manage the connection status, amount, recipient address, and transaction status. + +{@inject: examples/usdc-transfer-app/App.js#state} + +**Effect Hook for Connection Status** + +Use `useEffect` to update the connection status whenever the `currentAccount` value changes. + +{@inject: examples/usdc-transfer-app/App.js#useeffect} + +#### Token sending logic + +Define the function to handle sending tokens, including validation and transaction execution. + +{@inject: examples/usdc-transfer-app/App.js#variable=handleSendTokens} + +#### Rendering the UI + +Render the main UI components, including input fields for amount and recipient address, and a button to send tokens. + +{@inject: examples/usdc-transfer-app/App.js#ui} + +#### Complete HomeContent function + +After adding the UI components, your `HomeContent()` function is complete. Continue to the next section to add the function to the main application component. + +
+ +Complete `HomeContent()` function + +{@inject: examples/usdc-transfer-app/App.js#component=HomeContent} +
+ +### Main application component + +This component wraps the `HomeContent()` with necessary providers for state management and wallet connection. + +{@inject: examples/usdc-transfer-app/App.js#component=App} + +Save your `App.js` file and run `pnpm start` from your project root to start the development server (if not already running). + +Open [http://localhost:1234](http://localhost:1234) in your browser. Your UI should display and look similar to the following: + +![Stablecoin UI](./images/stablecoinui.png) + +
+ +Complete code for `App.js` + +{@inject: examples/usdc-transfer-app/App.js noComments} +
+ +### Connecting your wallet + +To connect your wallet: + +1. On the USDC Token Sender app, click the **Connect Wallet** button. +1. Select your wallet from the list of available options. +1. Approve the connection in your wallet extension. + + +### Performing a USDC transfer + +Follow these steps to perform a USDC transfer: + +1. Ensure you have USDC tokens in your wallet. You can get Testnet tokens from Circle's [faucet](https://faucet.circle.com/) if needed. +1. Click the **Request Testnet SUI Tokens** button from your Sui Wallet to source gas tokens. The button is visible only when your wallet is connected to Testnet. +1. In the app, enter the amount of USDC you want to send and the recipient address. +1. Click the **Send Tokens** button. +1. Your wallet prompts you to approve the transaction. Review the details and confirm. +1. Wait for the transaction to be processed. The app displays the transaction status. A successful transaction looks like the following: +
![Successful transaction](./images/stablecoinsuccess.png)
+ + +## Related links + +- [Regulated Coin and Deny List](./coin/regulated.mdx): Create a regulated coin and add or remove names from the deny list. +- [Loyalty Token](./coin/loyalty.mdx): Create a token to reward user loyalty. +- [In-Game Token](./coin/in-game-token.mdx): Create tokens that can be used only within a mobile game. \ No newline at end of file diff --git a/docs/content/guides/operator.mdx b/docs/content/guides/operator.mdx index d9326de7d37af..27e4409712ff9 100644 --- a/docs/content/guides/operator.mdx +++ b/docs/content/guides/operator.mdx @@ -25,9 +25,15 @@ Guides that benefit both Full node operators and validators include: - [Data Management](./operator/data-management.mdx) - [Genesis](./operator/genesis.mdx) +- [Monitoring](./operator/monitoring.mdx) - [Snapshots](./operator/snapshots.mdx) - [Archives](./operator/archives.mdx) ## Exchange integration guide -The [Sui Exchange Integration Guide](./operator/exchange-integration.mdx) provides step-by-step instructions on how to integrate SUI into a cryptocurrency exchange. \ No newline at end of file +The [Sui Exchange Integration Guide](./operator/exchange-integration.mdx) provides step-by-step instructions on how to integrate SUI into a cryptocurrency exchange. + +## Sui Bridge Node validators + +Guides that inform validators how to operate Sui Bridge include: +- [Sui Bridge Node Configuration](./operator/bridge-node-configuration.mdx) diff --git a/docs/content/guides/operator/data-management.mdx b/docs/content/guides/operator/data-management.mdx index a5460a001de39..eb3003f0b367d 100644 --- a/docs/content/guides/operator/data-management.mdx +++ b/docs/content/guides/operator/data-management.mdx @@ -94,6 +94,78 @@ To enable historic data queries for the Sui Full nodes that prune old transactio If the information about the transaction digest, effects, events, or checkpoints is not available locally, a Full node automatically retrieves the historical data from a cloud-based key-value store (currently managed by MystenLabs). Note that the current key-value store implementation keeps historic transactional data only: we plan to provide support for a similar setup for retrieving the historic object versions in a future release. +## Object pruning {#object-pruning} + +Sui adds new object versions to the database as part of transaction execution. This makes previous versions ready for +garbage collection. However, without pruning, this can result in database performance degradation and requires large +amounts of storage space. Sui identifies the objects that are eligible for pruning in each checkpoint, and then performs +the pruning in the background. + +You can enable pruning for a Sui node by adding the `authority-store-pruning-config` config to `fullnode.yaml` file: +```yaml +authority-store-pruning-config: + # Number of epoch dbs to keep + # Not relevant for object pruning + num-latest-epoch-dbs-to-retain: 3 + # The amount of time, in seconds, between running the object pruning task. + # Not relevant for object pruning + epoch-db-pruning-period-secs: 3600 + # Number of epochs to wait before performing object pruning. + # When set to 0, Sui prunes old object versions as soon + # as possible. This is also called *aggressive pruning*, and results in the most effective + # garbage collection method with the lowest disk usage possible. + # This is the recommended setting for Sui Validator nodes since older object versions aren't + # necessary to execute transactions. + # When set to 1, Sui prunes only object versions from transaction checkpoints + # previous to the current epoch. In general, when set to N (where N >= 1), Sui prunes + # only object versions from checkpoints up to `current - N` epoch. + # It is therefore possible to have multiple versions of an object present + # in the database. This setting is recommended for Sui Full nodes as they might need to serve + # RPC requests that require looking up objects by ID and Version (rather than just latest + # version). However, if your Full node does not serve RPC requests you should then also enable + # aggressive pruning. + num-epochs-to-retain: 1 + # Advanced setting: Maximum number of checkpoints to prune in a batch. The default + # settings are appropriate for most use cases. + max-checkpoints-in-batch: 10 + # Advanced setting: Maximum number of transactions in one batch of pruning run. The default + # settings are appropriate for most use cases. + max-transactions-in-batch: 1000 +``` +## Transaction pruning {#transaction-pruning} + +Transaction pruning removes previous transactions and effects from the database. +Sui periodically creates checkpoints. Each checkpoint contains the transactions that occurred during the checkpoint and their associated effects. + +Sui performs transaction pruning in the background after checkpoints complete. + +You can enable transaction pruning for your Full node or Validator node by adding `num-epochs-to-retain-for-checkpoints` +to the `authority-store-pruning-config` config for the node: + +```yaml +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 + # Number of epochs to wait before performing transaction pruning. + # When this is N (where N >= 2), Sui prunes transactions and effects from + # checkpoints up to the `current - N` epoch. Sui never prunes transactions and effects from the current and + # immediately prior epoch. N = 2 is a recommended setting for Sui Validator nodes and Sui Full nodes that don't + # serve RPC requests. + num-epochs-to-retain-for-checkpoints: 2 + # Ensures that individual database files periodically go through the compaction process. + # This helps reclaim disk space and avoid fragmentation issues + periodic-compaction-threshold-days: 1 +``` + +:::info + +If you prune transactions, Archival nodes can help ensure lagging peer nodes don't lose any information. For more information, see [Sui Archives](./archives.mdx). + +::: + ## Pruning policy examples diff --git a/docs/content/guides/operator/monitoring.mdx b/docs/content/guides/operator/monitoring.mdx new file mode 100644 index 0000000000000..236e6b72ec648 --- /dev/null +++ b/docs/content/guides/operator/monitoring.mdx @@ -0,0 +1,25 @@ +--- +title: Sui Node Monitoring +description: Monitor Sui node metrics to ensure the health and performance of your node. +--- + +:::info + +These instructions are for advanced users. If you just need a local development environment, you should instead follow the instructions in [Create a Local Sui Network](../developer/getting-started/local-network.mdx) to create a local Full node, validators, and faucet. + +::: + +Nodes expose on `localhost:9184/metrics` by default. + +You can view the metrics in the metrics UI, or you can use a tool like `curl` to get the metrics in a format that is easy to parse. + +```bash +curl -s http://localhost:9184/metrics | grep -E 'sui_validator|sui_fullnode' +``` + +## Production monitoring + +For production monitoring, we recommend using [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/). + +You can use grafana agent, grafana alloy, or another tool to scrape the metrics from your node. + diff --git a/docs/content/guides/operator/sui-full-node.mdx b/docs/content/guides/operator/sui-full-node.mdx index 7de2c205d8c02..732a159269bd1 100644 --- a/docs/content/guides/operator/sui-full-node.mdx +++ b/docs/content/guides/operator/sui-full-node.mdx @@ -42,7 +42,7 @@ Validator nodes store only the latest transactions on the frontier of the object ## Full node setup -Follow the instructions here to run your own Sui Full node. +Follow the instructions here to run your own Sui Full node. Sui Full nodes run using the `sui-node` binary. ### Hardware requirements @@ -54,7 +54,8 @@ Suggested minimum hardware to run a Sui Full node: ### Software requirements -Sui recommends running Sui Full nodes on Linux. Sui supports the Ubuntu and Debian distributions. You can also run a Sui Full node on macOS. +Sui recommends running Sui Full nodes on Linux. Sui supports the Ubuntu and Debian distributions. You can run a Sui Full node on macOS, +but this is only recommended for development and not for production use. Make sure to [update Rust](https://doc.rust-lang.org/book/ch01-01-installation.html#updating-and-uninstalling). @@ -78,34 +79,33 @@ clang \ cmake ``` -## Configure a Full node +## Running a Full node {#running-a-full-node} + +Instructions for building, installing, or downloading the `sui-node` binary are available at [Sui Install](../developer/getting-started/sui-install.mdx). +These install instructions are specific to the `sui` cli, but apply to the `sui-node` binary as well. + +There are many ways to run a Sui Full node (bare metal, virtual machine, Kubernetes statefulset, and so on), and the solution that you choose depends on your specific needs as well as the infrastructure that you have available. + +There are some specific considerations to keep in mind when running a Sui Full node that apply to all environments: + +* [Genesis](./genesis.mdx): You must download the genesis blob for the network that you want to connect to, and make it available to the `sui-node`. +* [Data Storage](./data-management.mdx): Sui Full nodes _can_ require a significant amount of disk space to store the blockchain history. If you plan to use your Full node to serve RPC requests, you must also plan for the storage of index files, which requires a significant amount of disk space. +* [Monitoring](./monitoring.mdx): Sui Full nodes expose metrics about the node's health and the state of the Sui network. +* [Updates](./updates.mdx): Sui Full nodes must be updated to the latest version to remain in sync with the network. +* [Archival Fallback](./archives.mdx): The archival fallback allows you to sync checkpoints from any point in the chain's history. The network `seed-peers` below only keep a few epochs of history. -You can configure a Sui Full node either using Docker or by building from source. ### Using Docker Compose -Follow the instructions in the [Full node Docker Readme](https://github.com/MystenLabs/sui/tree/main/docker/fullnode#readme) to run a Sui Full node using Docker, including [resetting the environment](https://github.com/MystenLabs/sui/tree/main/docker/fullnode#reset-the-environment). +There's a guide in the Sui repository on running a Full node via [Docker Compose](https://github.com/MystenLabs/sui/tree/main/docker/fullnode#readme). +This alone is not suitable for a production environment, but can be used to get a Full node up and running quickly on a virtual machine or local machine for development purposes. +Refer to [Running a Full node](#running-a-full-node) for instructions relevant to production use cases. -### Setting up a local Sui repository -You must get the latest source files from the Sui GitHub repository. -1. Set up your fork of the Sui repository: - 1. Go to the [Sui repository](https://github.com/MystenLabs/sui) on GitHub and click the Fork button in the top right-hand corner of the screen. - 1. Clone your personal fork of the Sui repository to your local machine (ensure that you insert your GitHub username into the URL): - `git clone https://github.com//sui.git` -1. `cd` into your `sui` repository: - `cd sui` -1. Set up the Sui repository as a git remote: - `git remote add upstream https://github.com/MystenLabs/sui` -1. Sync your fork: - `git fetch upstream` -1. Check out the branch associated with the network version you want to run (for example, `devnet` to run a Devnet Full node): - `git checkout --track upstream/` +### Setting up a Full node -### Setting up a Full node from source {#set-up-from-source} +When you are ready to run `sui-node` in your production environment, you can set up your Full node by completing the following steps: -Open a terminal or console to the `sui` directory you downloaded in the previous steps to complete the following: -1. Install the required prerequisites. 1. Make a copy of the [Full node YAML template](https://github.com/MystenLabs/sui/blob/main/crates/sui-config/data/fullnode-template.yaml): `cp crates/sui-config/data/fullnode-template.yaml fullnode.yaml` 1. Download the genesis blob for the network to use: @@ -171,7 +171,7 @@ Open a terminal or console to the `sui` directory you downloaded in the previous - +1. Optional: Set up the [Archival Fallback](./archives.mdx), which allows you to sync checkpoints if you fall behind the network's `seed-peers`. 1. Optional: Skip this step to accept the default paths to resources. Edit the fullnode.yaml file to use custom paths. 1. Update the `db-path` field with the path to the Full node database. `db-path: "/db-files/sui-fullnode"` @@ -181,18 +181,19 @@ Open a terminal or console to the `sui` directory you downloaded in the previous genesis-file-location: "/sui-fullnode/genesis.blob" ``` -### Starting services +### Starting your Full node {#starting-a-full-node} + +You should not start syncing your Full node from the start of the genesis. This will take a very long time and consume a lot of resources (including likely filling up your disk). -At this point, your Sui Full node is ready to connect to the Sui network. +Instead, start your Full node from a recent snapshot. You can find details on how to obtain a snapshot from the [Sui Snapshots guide](./snapshots.mdx). -1. Open a terminal or console to the sui directory. -1. Start the Sui Full node: - `cargo run --release --bin sui-node -- --config-path fullnode.yaml` -1. Optional: Publish/subscribe to notifications using JSON-RPC via websocket. +Now that you have your Full node config file set up, and you've obtained a snapshot, you can start your Full node by running the `sui-node` binary with your `fullnode.yaml` configuration file: -If your setup is successful, your Sui Full node is now connected to the appropriate network. +```shell +sui-node --config-path fullnode.yaml +``` -Your Full node serves the read endpoints of the Sui JSON-RPC API at: `http://127.0.0.1:9000`. +It's a good idea to use something like systemd to manage your Full node in a production environment. ### Troubleshooting If you receive a `cannot find -lpq` error, you are missing the `libpq` library. Use `sudo apt-get install libpq-dev` to install on Linux, or `brew install libpq` on MacOS. After you install on MacOS, create a Homebrew link using `brew link --force libpq`. For further context, reference the [issue on Stack Overflow](https://stackoverflow.com/questions/70313347/ld-library-not-found-for-lpq-when-build-rust-in-macos?rq=1). @@ -208,131 +209,3 @@ Then update the metrics address in your fullnode.yaml file to use port `9180`. ```shell metrics-address: "0.0.0.0:9180" ``` - -## Monitoring - -Monitor your Full node using the instructions at Logging, Tracing, Metrics, and Observability. - -The default metrics port is `9184`. To change the port, edit your fullnode.yaml file. - -## Update your Full node - -Whenever Sui releases a new version, you must update your Full node with the release to ensure compatibility with the network it connects to. For example, if you use Sui Testnet you should install the version of Sui running on Sui Testnet. - -### Update with Docker Compose - -Follow the instructions to [reset the environment](https://github.com/MystenLabs/sui/tree/main/docker/fullnode#reset-the-environment), namely by running the command: - -```shell -docker-compose down --volumes -``` - -### Update from source - -If you followed the instructions for Building from Source, use the following steps to update your Full node: - -1. Shut down your running Full node. -1. `cd` into your local Sui repository: - ```shell - cd sui - ``` -1. Remove the database and 'genesis.blob' file: - ```shell - rm -r suidb genesis.blob - ``` -1. Fetch the source from the latest release: - ```shell - git fetch upstream - ``` -1. Reset your branch: - ```shell - git checkout -B --track upstream/ - ``` -1. Download the latest genesis blob: - - [Devnet genesis blob](https://github.com/MystenLabs/sui-genesis/raw/main/devnet/genesis.blob): - ```shell - curl -fLJO https://github.com/MystenLabs/sui-genesis/raw/main/devnet/genesis.blob - ``` - - [Testnet genesis blob](https://github.com/MystenLabs/sui-genesis/raw/main/testnet/genesis.blob): - ```shell - curl -fLJO https://github.com/MystenLabs/sui-genesis/raw/main/testnet/genesis.blob - ``` -1. Update your fullnode.yaml configuration file, if needed. -1. Restart your Sui Full node: - ```shell - cargo run --release --bin sui-node -- --config-path fullnode.yaml - ``` - -Your Full node starts on: http://127.0.0.1:9000. - -## Object pruning {#object-pruning} - -Sui adds new object versions to the database as part of transaction execution. This makes previous versions ready for -garbage collection. However, without pruning, this can result in database performance degradation and requires large -amounts of storage space. Sui identifies the objects that are eligible for pruning in each checkpoint, and then performs -the pruning in the background. - -You can enable pruning for a Sui node by adding the `authority-store-pruning-config` config to `fullnode.yaml` file: -```yaml -authority-store-pruning-config: - # Number of epoch dbs to keep - # Not relevant for object pruning - num-latest-epoch-dbs-to-retain: 3 - # The amount of time, in seconds, between running the object pruning task. - # Not relevant for object pruning - epoch-db-pruning-period-secs: 3600 - # Number of epochs to wait before performing object pruning. - # When set to 0, Sui prunes old object versions as soon - # as possible. This is also called *aggressive pruning*, and results in the most effective - # garbage collection method with the lowest disk usage possible. - # This is the recommended setting for Sui Validator nodes since older object versions aren't - # necessary to execute transactions. - # When set to 1, Sui prunes only object versions from transaction checkpoints - # previous to the current epoch. In general, when set to N (where N >= 1), Sui prunes - # only object versions from checkpoints up to `current - N` epoch. - # It is therefore possible to have multiple versions of an object present - # in the database. This setting is recommended for Sui Full nodes as they might need to serve - # RPC requests that require looking up objects by ID and Version (rather than just latest - # version). However, if your Full node does not serve RPC requests you should then also enable - # aggressive pruning. - num-epochs-to-retain: 0 - # Advanced setting: Maximum number of checkpoints to prune in a batch. The default - # settings are appropriate for most use cases. - max-checkpoints-in-batch: 10 - # Advanced setting: Maximum number of transactions in one batch of pruning run. The default - # settings are appropriate for most use cases. - max-transactions-in-batch: 1000 -``` -## Transaction pruning {#transaction-pruning} - -Transaction pruning removes previous transactions and effects from the database. -Sui periodically creates checkpoints. Each checkpoint contains the transactions that occurred during the checkpoint and their associated effects. - -Sui performs transaction pruning in the background after checkpoints complete. - -You can enable transaction pruning for your Full node or Validator node by adding `num-epochs-to-retain-for-checkpoints` -to the `authority-store-pruning-config` config for the node: - -```yaml -authority-store-pruning-config: - num-latest-epoch-dbs-to-retain: 3 - epoch-db-pruning-period-secs: 3600 - num-epochs-to-retain: 0 - max-checkpoints-in-batch: 10 - max-transactions-in-batch: 1000 - # Number of epochs to wait before performing transaction pruning. - # When this is N (where N >= 2), Sui prunes transactions and effects from - # checkpoints up to the `current - N` epoch. Sui never prunes transactions and effects from the current and - # immediately prior epoch. N = 2 is a recommended setting for Sui Validator nodes and Sui Full nodes that don't - # serve RPC requests. - num-epochs-to-retain-for-checkpoints: 2 - # Ensures that individual database files periodically go through the compaction process. - # This helps reclaim disk space and avoid fragmentation issues - periodic-compaction-threshold-days: 1 -``` - -:::info - -If you prune transactions, Archival nodes can help ensure lagging peer nodes don't lose any information. For more information, see [Sui Archives](./archives.mdx). - -::: diff --git a/docs/content/guides/operator/updates.mdx b/docs/content/guides/operator/updates.mdx new file mode 100644 index 0000000000000..26c362721d9ce --- /dev/null +++ b/docs/content/guides/operator/updates.mdx @@ -0,0 +1,18 @@ +--- +title: Updating a Full Node +description: Update your Sui Full node to the latest version to remain in sync with the network. +--- + +## Release process + +Whenever Sui releases a new version, you must update your Full node with the release to ensure compatibility with the network it connects to. For example, if you use Sui Testnet you should install the version of Sui running on Sui Testnet. + +Any release that contains a protocol change will need to be followed before the protocol upgrade takes place (when enough stake within the validator set upgrades, the new protocol version is enacted in the next epoch). +If you do not update your Full node, you will not be able to connect to the network after the protocol upgrade takes place. + +## Update your Full node + +You can track the latest version of Sui on the [Sui Releases](https://github.com/MystenLabs/sui/releases) page on GitHub. +The schedule for each network is available in the [Network Release Schedule](https://sui.io/networkinfo) page. + +It is reasonable to have to shut down your Full node to perform an update, whether that be a rolling restart in Kubernetes, or a systemctl stop on a Linux machine to replace the sui-node binary. diff --git a/docs/content/sidebars/guides.js b/docs/content/sidebars/guides.js index b30dead8f86f8..9b981ba0d7f33 100644 --- a/docs/content/sidebars/guides.js +++ b/docs/content/sidebars/guides.js @@ -85,6 +85,7 @@ const guides = [ 'guides/developer/coin/loyalty', ], }, + 'guides/developer/stablecoins', { type: 'category', label: 'NFTs', @@ -192,15 +193,17 @@ const guides = [ items: [ 'guides/operator/sui-full-node', 'guides/operator/validator-config', - 'guides/operator/bridge-node-configuration', + 'guides/operator/genesis', + 'guides/operator/monitoring', + 'guides/operator/updates', 'guides/operator/data-management', 'guides/operator/snapshots', 'guides/operator/archives', - 'guides/operator/genesis', - 'guides/operator/validator-committee', - 'guides/operator/validator-tasks', 'guides/operator/node-tools', 'guides/operator/exchange-integration', + 'guides/operator/bridge-node-configuration', + 'guides/operator/validator-committee', + 'guides/operator/validator-tasks', ], }, ]; diff --git a/docs/content/standards/deepbookv3-sdk.mdx b/docs/content/standards/deepbookv3-sdk.mdx index 65090fbffa2e5..3e19f37a19aea 100644 --- a/docs/content/standards/deepbookv3-sdk.mdx +++ b/docs/content/standards/deepbookv3-sdk.mdx @@ -2,7 +2,14 @@ title: DeepBookV3 SDK --- -The DeepBook typescript SDK abstracts away the transaction calls, allowing for direct interactions with the DeepBook package. To use the SDK in your projects, install the `@mysten/deepbook` package. +The DeepBook typescript SDK abstracts away the transaction calls, allowing for direct interactions with the DeepBook package. + +- [SDK Repository](https://github.com/MystenLabs/sui/tree/main/sdk/deepbook-v3) +- [NPM version](https://www.npmjs.com/package/@mysten/deepbook-v3) + +## Install + +To use the SDK in your projects, install the `@mysten/deepbook` package. ```sh npm2yarn npm install @mysten/deepbook-v3 diff --git a/docs/content/standards/deepbookv3-sdk/pools.mdx b/docs/content/standards/deepbookv3-sdk/pools.mdx index 2577a73c72bce..03befb48d7d2e 100644 --- a/docs/content/standards/deepbookv3-sdk/pools.mdx +++ b/docs/content/standards/deepbookv3-sdk/pools.mdx @@ -13,9 +13,58 @@ The DeepBookV3 SDK exposes functions that you can call to read the state of a po Decimal adjust all input quantities. All outputs are decimal adjusted. ::: +### account + +Use `account` to retrieve the account information for a `BalanceManager` in a pool, which has the following form: + +```tsx +{ + epoch: '511', + open_orders: { + constants: [ + '170141211130585342296014727715884105730', + '18446744092156295689709543266', + '18446744092156295689709543265' + ] + }, + taker_volume: 0, + maker_volume: 0, + active_stake: 0, + inactive_stake: 0, + created_proposal: false, + voted_proposal: null, + unclaimed_rebates: { base: 0, quote: 0, deep: 0 }, + settled_balances: { base: 0, quote: 0, deep: 0 }, + owed_balances: { base: 0, quote: 0, deep: 0 } +} +``` + +**Parameters** + +- `poolKey`: String that identifies the pool to query. +- `balanceManagerKey`: key of the balance manager defined in the SDK. + +```tsx +async account(poolKey: string, managerKey: string) {} +``` + +### accountOpenOrders + +Use `accountOpenOrders` to retrieve open orders for the balance manager and pool with the IDs you provide. The call returns a `Promise` that contains an array of open order IDs. + +**Parameters** + +- `poolKey`: String that identifies the pool to query. +- `managerKey`: String that identifies the balance manager to query. + +```tsx +async accountOpenOrders(poolKey: string, managerKey: string) {} +``` + ### checkManagerBalance Use `checkManagerBalance` to check the balance manager for a specific coin. The call returns a `Promise` in the form: + ``` { coinType: string, @@ -62,7 +111,6 @@ Use `getOrder` to retrieve an order's information. The call returns a `Promise` async getOrder(poolKey: string, orderId: string) {} ``` - ### getQuoteQuantityOut Use `getQuoteQuantityOut` to retrieve the quote quantity out for the base quantity you provide. The call returns a `Promise` in the form: @@ -131,19 +179,6 @@ where `deepRequired` is the amount of DEEP required for the dry run. async getQuantityOut(poolKey: string, baseQuantity: number, quoteQuantity: number) {} ``` -### accountOpenOrders - -Use `accountOpenOrders` to retrieve open orders for the balance manager and pool with the IDs you provide. The call returns a `Promise` that contains an array of open order IDs. - -**Parameters** - -- `poolKey`: String that identifies the pool to query. -- `managerKey`: String that identifies the balance manager to query. - -```tsx -async accountOpenOrders(poolKey: string, managerKey: string) {} -``` - ### getLevel2Range Use `getLevel2Range` to retrieve level 2 order book within the boundary price range you provide. The call returns a `Promise` in the form: @@ -186,10 +221,51 @@ Use `getLevel2TicksFromMid` to retrieve level 2 order book ticks from mid-price async getLevel2TicksFromMid(poolKey: string, ticks: number) {} ``` +### lockedBalance + +Use `lockedBalance` to retrieve a `BalanceManager` locked balance in the pool. The call returns a `Promise` in the `Order` struct, which has the following form: + +```tsx +{ + base: 5.5, + quote: 2, + deep: 0.15, +} +``` + +**Parameters** + +`poolKey`: String that identifies the pool to query. `balanceManagerKey`: key of the balance manager defined in the SDK. + +```tsx +async lockedBalance(poolKey: string, balanceManagerKey: string) {} +``` + +### poolTradeParams + +Use `poolTradeParams` to retrieve the trade params for the pool, which has the following form: + +```tsx +{ + takerFee: 0.001, + makerFee: 0.0005, + stakeRequired: 100, +} +``` + +**Parameters** + +- `poolKey`: String that identifies the pool to query. + +```tsx +async poolTradeParams(poolKey: string) {} +``` + ### vaultBalances Use `vaultBalances` to get the vault balances for a pool with the ID you provide. The call returns a `Promise` in the form: -``` + +```tsx { base: number, quote: number, diff --git a/docs/content/standards/deepbookv3/query-the-pool.mdx b/docs/content/standards/deepbookv3/query-the-pool.mdx index e8b80f796cadf..24ad2495c0591 100644 --- a/docs/content/standards/deepbookv3/query-the-pool.mdx +++ b/docs/content/standards/deepbookv3/query-the-pool.mdx @@ -57,6 +57,18 @@ public fun get_quantity_out( ): (u64, u64, u64) ``` +### Check fee required + +Returns the DEEP required for an order if it's a taker or maker given quantity and price (`deep_required_taker`, `deep_required_maker`). + +```move +public fun get_order_deep_required( + self: &Pool, + base_quantity: u64, + price: u64, +): (u64, u64) +``` + ### Retrieve mid price for a pool Returns the mid price of the pool. @@ -111,10 +123,76 @@ public fun vault_balances( ): (u64, u64, u64) ``` -### Retrieve Pool ID +### Retrieve pool ID Get the ID of the pool given the asset types. ```move public fun get_pool_id_by_asset(registry: &Registry): ID +``` + +### Retrieve order information + +Returns the `Order` struct using the order ID. + +```move +public fun get_order( + self: &Pool, + order_id: u128, +): Order +``` + +Returns a vector of `Order` structs using a vector of order IDs. + +```move +public fun get_orders( + self: &Pool, + order_ids: vector, +): vector +``` + +Returns a vector of `Order` structs for all orders that belong to a `BalanceManager` in the pool. + +```move +public fun get_account_order_details( + self: &Pool, + balance_manager: &BalanceManager, +): vector +``` + +### Retrieve locked balance + +Returns the locked balance for a `BalanceManager` in the pool (`base_quantity`, `quote_quantity`, `deep_quantity`). + +```move +public fun locked_balance( + self: &Pool, + balance_manager: &BalanceManager, +): (u64, u64, u64) +``` + +### Retrieve pool parameters + +Returns the trade parameters for the pool (`taker_fee`, `maker_fee`, `stake_required`). + +```move +public fun pool_trade_params( + self: &Pool, +): (u64, u64, u64) +``` + +Returns the book parameters for the pool (`tick_size`, `lot_size`, `min_size`). + +```move +public fun pool_book_params( + self: &Pool, +): (u64, u64, u64) +``` + +Returns the `OrderDeepPrice` struct for the pool, which determines the conversion for DEEP fees. + +```move +public fun get_order_deep_price( + self: &Pool, +): OrderDeepPrice ``` \ No newline at end of file diff --git a/docs/site/src/pages/index.js b/docs/site/src/pages/index.js index 7fa05c6824877..081e10cfc486c 100644 --- a/docs/site/src/pages/index.js +++ b/docs/site/src/pages/index.js @@ -70,6 +70,12 @@ export default function Home() { Run a Sui Full node + + Sui Bridge Node configuration + diff --git a/docs/site/src/plugins/inject-code/injectLoader.js b/docs/site/src/plugins/inject-code/injectLoader.js index ffa49efdbfe77..4c7629338b4b4 100644 --- a/docs/site/src/plugins/inject-code/injectLoader.js +++ b/docs/site/src/plugins/inject-code/injectLoader.js @@ -65,7 +65,9 @@ const addCodeInject = function (source) { const isTs = language === "ts" || language === "js"; if (fs.existsSync(fullPath)) { - let injectFileContent = fs.readFileSync(fullPath, "utf8"); + let injectFileContent = fs + .readFileSync(fullPath, "utf8") + .replaceAll(`\t`, " "); const marker = injectFileFull.indexOf("#") > 0 ? injectFileFull.substring(injectFileFull.indexOf("#")) @@ -178,13 +180,15 @@ const addCodeInject = function (source) { } }); let varContent = []; - if (language === "ts") { + if (language === "ts" || language === "js") { const varTsFunction = `^( *)?.*?(let|const) \\b${variableName}\\b.*=>`; + const varTsVariable = `^( *)?.*?(let|const) \\b${variableName}\\b (?!.*=>)=.*;`; const varTsRE = new RegExp(varTsFunction, "m"); + const varTsVarRE = new RegExp(varTsVariable, "m"); const varTsMatch = varTsRE.exec(injectFileContent); + const varTsVarMatch = varTsVarRE.exec(injectFileContent); if (varTsMatch) { const start = injectFileContent.slice(varTsMatch.index); - //console.log(varTsMatch[1]); const endText = `^${varTsMatch[1] ? varTsMatch[1] : ""}\\)?\\};`; const endRE = new RegExp(endText, "m"); const endMatch = endRE.exec(start); @@ -198,6 +202,14 @@ const addCodeInject = function (source) { preVarTs, ), ); + } else if (varTsVarMatch) { + let preVarTs2 = utils.capturePrepend( + varTsVarMatch, + injectFileContent, + ); + varContent.push( + utils.removeLeadingSpaces(varTsVarMatch[0], preVarTs2), + ); } } else { for (let v of groupedVars) { @@ -266,7 +278,7 @@ const addCodeInject = function (source) { element = names[1]; ordinal = names[2] ? names[2] : ""; } - const compStr = `^( *)(export (default )?)?function \\b${name}\\b.*?\\n\\1}\\n`; + const compStr = `^( *)(export (default )?)?function \\b${name}\\b.*?\\n\\1\\}\\n`; const compRE = new RegExp(compStr, "ms"); const compMatch = compRE.exec(injectFileContent); if (compMatch) { @@ -506,6 +518,7 @@ const addCodeInject = function (source) { language, injectFile, processed, + options, ); // Temporarily replace double spaces with tabs. Replaced back downstream. // Prevents unexpected whitespace removal from util functions. diff --git a/examples/custom-indexer/rust/local_reader.rs b/examples/custom-indexer/rust/local_reader.rs index 6d372749f3600..17f93f3d33e7d 100644 --- a/examples/custom-indexer/rust/local_reader.rs +++ b/examples/custom-indexer/rust/local_reader.rs @@ -16,6 +16,7 @@ struct CustomWorker; #[async_trait] impl Worker for CustomWorker { + type Result = (); async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { // custom processing logic println!("Processing Local checkpoint: {}", checkpoint.checkpoint_summary.to_string()); diff --git a/examples/custom-indexer/rust/remote_reader.rs b/examples/custom-indexer/rust/remote_reader.rs index 4c55fcb960772..ed91f1523a5de 100644 --- a/examples/custom-indexer/rust/remote_reader.rs +++ b/examples/custom-indexer/rust/remote_reader.rs @@ -10,6 +10,7 @@ struct CustomWorker; #[async_trait] impl Worker for CustomWorker { + type Result = (); async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { // custom processing logic // print out the checkpoint number diff --git a/examples/usdc-transfer-app/App.js b/examples/usdc-transfer-app/App.js new file mode 100644 index 0000000000000..1cace7e744471 --- /dev/null +++ b/examples/usdc-transfer-app/App.js @@ -0,0 +1,163 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// docs::#setup +import React, { useState, useEffect } from "react"; +import { + createNetworkConfig, + SuiClientProvider, + useSuiClient, + ConnectButton, + useCurrentAccount, + useSignAndExecuteTransaction, + WalletProvider, +} from "@mysten/dapp-kit"; +import { Transaction } from "@mysten/sui/transactions"; +import { getFullnodeUrl } from "@mysten/sui/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState } from "react"; +import "@mysten/dapp-kit/dist/index.css"; +// docs::/#setup + +const { networkConfig } = createNetworkConfig({ + testnet: { + url: getFullnodeUrl("testnet"), + }, + mainnet: { + url: getFullnodeUrl("mainnet"), + }, +}); + +// Create a new QueryClient for managing and caching asynchronous queries +const queryClient = new QueryClient(); + +// Define the USDC token type on Sui Testnet +// This is the unique identifier for the USDC token on Sui +const USDC_TYPE = '0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC'; + +function HomeContent() { + // docs::#state + // Use the wallet kit to get the current account and transaction signing function + const currentAccount = useCurrentAccount(); + const { mutate: signAndExecuteTransaction } = useSignAndExecuteTransaction(); + // Get the Sui client for interacting with the Sui network + const suiClient = useSuiClient(); + const [open, setOpen] = useState(false); + const [connected, setConnected] = useState(false); + const [amount, setAmount] = useState(""); + const [recipientAddress, setRecipientAddress] = useState(""); + const [txStatus, setTxStatus] = useState(""); + // docs::/#state + + // docs::#useeffect + useEffect(() => { + setConnected(!!currentAccount); + }, [currentAccount]); + // docs::/#useeffect + + const handleSendTokens = async () => { + if (!currentAccount || !amount || !recipientAddress) { + setTxStatus("Please connect wallet and fill in all fields"); + return; + } + try { + // Fetch USDC coins owned by the current account + // This uses the SuiClient to get coins of the specified type owned by the current address + const { data: coins } = await suiClient.getCoins({ + owner: currentAccount.address, + coinType: USDC_TYPE, + }); + if (coins.length === 0) { + setTxStatus("No USDC coins found in your wallet"); + return; + } + // Create a new transaction block + // Transaction is used to construct and execute transactions on Sui + const tx = new Transaction(); + // Convert amount to smallest unit (6 decimals) + const amountInSmallestUnit = BigInt(parseFloat(amount) * 1_000_000); + // Split the coin and get a new coin with the specified amount + // This creates a new coin object with the desired amount to be transferred + const [coin] = tx.splitCoins(coins[0].coinObjectId, [ + tx.pure.u64(amountInSmallestUnit), + ]); + // Transfer the split coin to the recipient + // This adds a transfer operation to the transaction block + tx.transferObjects([coin], tx.pure.address(recipientAddress)); + // Sign and execute the transaction block + // This sends the transaction to the network and waits for it to be executed + const result = await signAndExecuteTransaction( + { + transaction: tx, + }, + { + onSuccess: (result) => { + console.log("Transaction result:", result); + setTxStatus(`Transaction successful. Digest: ${result.digest}`); + }, + } + ); + } catch (error) { + console.error("Error sending tokens:", error); + setTxStatus( + `Error: ${error instanceof Error ? error.message : "Unknown error"}` + ); + } + }; + + // docs::#ui + return ( +
+
+

Sui USDC Sender (Testnet)

+ + {connected && currentAccount && ( +

Connected: {currentAccount.address}

+ )} +
+ setAmount(e.target.value)} + className="input" + /> + setRecipientAddress(e.target.value)} + className="input" + /> + +
+ {txStatus &&

{txStatus}

} +
+
+ ); + // docs::/#ui +} + +function App() { + return ( + + + + + + + + ); +} + +export default App; diff --git a/examples/usdc-transfer-app/favicon.ico b/examples/usdc-transfer-app/favicon.ico new file mode 100644 index 0000000000000..d664e37e793e5 Binary files /dev/null and b/examples/usdc-transfer-app/favicon.ico differ diff --git a/examples/usdc-transfer-app/global.css b/examples/usdc-transfer-app/global.css new file mode 100644 index 0000000000000..f8deec0ab6fc8 --- /dev/null +++ b/examples/usdc-transfer-app/global.css @@ -0,0 +1,64 @@ +.mainwrapper { + display: flex; + flex-direction: column; + min-height: 100vh; + padding: 6rem; +} + +.outerwrapper { + z-index: 10; + width: 100%; + max-width: 64rem; + margin: 0 auto; + justify-content: center; + justify-content: space-between; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.h1 { + font-size: 2.25rem; + line-height: 2.5rem; + font-weight: bold; + margin-bottom: 2rem; +} + +.status { + margin-top: 1rem +} + +.form { + margin-top: 2rem +} + +.input { + padding: 0.5rem; + border-width: 1px; + border-radius: 0.25rem; + margin-right: 0.5rem; + color: black; +} + +.connected { + background-color: rgb(191 219 254); + color: black; +} + +.connected:hover { + background-color: rgb(147 197 253); +} + +.notconnected { + background-color: rgb(209 213 219); + color: rgb(107 114 128); +} + +.transition { + padding: 0.5rem; + border-radius: 0.25rem; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-duration: 200ms; +} \ No newline at end of file diff --git a/examples/usdc-transfer-app/index.html b/examples/usdc-transfer-app/index.html new file mode 100644 index 0000000000000..5da58944459bd --- /dev/null +++ b/examples/usdc-transfer-app/index.html @@ -0,0 +1,14 @@ + + + + + + Sui USDC Sender + + + + +
+ + + \ No newline at end of file diff --git a/examples/usdc-transfer-app/index.js b/examples/usdc-transfer-app/index.js new file mode 100644 index 0000000000000..1bd33d2398271 --- /dev/null +++ b/examples/usdc-transfer-app/index.js @@ -0,0 +1,10 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; +import App from './App.js'; + +const root = createRoot(document.getElementById('root')); +root.render(); \ No newline at end of file diff --git a/examples/usdc-transfer-app/lib/App-stub.js b/examples/usdc-transfer-app/lib/App-stub.js new file mode 100644 index 0000000000000..54bc2d27e499a --- /dev/null +++ b/examples/usdc-transfer-app/lib/App-stub.js @@ -0,0 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// This file is used only for instruction. + +import React from 'react'; + +function App() { + return ( +
+

Hello, World!

+

This is a minimal React app.

+
+ ); +} + +export default App; \ No newline at end of file diff --git a/examples/usdc-transfer-app/package.json b/examples/usdc-transfer-app/package.json new file mode 100644 index 0000000000000..eead21e3af1f0 --- /dev/null +++ b/examples/usdc-transfer-app/package.json @@ -0,0 +1,23 @@ +{ + "name": "usdc-transfer-app", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "parcel index.html" + }, + "keywords": [], + "author": "", + "license": "Apache 2.0", + "dependencies": { + "@mysten/dapp-kit": "^0.14.25", + "@mysten/sui": "^1.12.0", + "@tanstack/react-query": "^5.50.1", + "parcel": "^2.12.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "process": "^0.11.10" + } +} diff --git a/external-crates/move/crates/move-analyzer/editors/code/language-configuration.json b/external-crates/move/crates/move-analyzer/editors/code/language-configuration.json index 5b20cd79bc16d..60ae28d100f85 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/language-configuration.json +++ b/external-crates/move/crates/move-analyzer/editors/code/language-configuration.json @@ -12,7 +12,7 @@ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, { "open": "/*", "close": "*/", "notIn": ["string"] } ], - "brackets": + "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] @@ -27,5 +27,5 @@ ["(", ")"], ["<", ">"], ["\"", "\""] - ], + ] } diff --git a/external-crates/move/crates/move-analyzer/editors/code/package-lock.json b/external-crates/move/crates/move-analyzer/editors/code/package-lock.json index ebb58e03531da..70e287b7a59e4 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package-lock.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package-lock.json @@ -1,12 +1,12 @@ { "name": "move", - "version": "1.0.10", + "version": "1.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "move", - "version": "1.0.10", + "version": "1.0.13", "license": "Apache-2.0", "dependencies": { "command-exists": "^1.2.9", @@ -701,12 +701,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1541,10 +1542,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2098,6 +2100,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -2357,12 +2360,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3391,6 +3395,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/external-crates/move/crates/move-analyzer/editors/code/package.json b/external-crates/move/crates/move-analyzer/editors/code/package.json index 43a0b2ebd39d5..b97675a005c68 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package.json @@ -124,13 +124,13 @@ "menus": { "commandPalette": [ { - "command": "sui.serverVersion" + "command": "move.serverVersion" }, { - "command": "sui.build" + "command": "move.build" }, { - "command": "sui.test" + "command": "move.test" } ] } diff --git a/external-crates/move/crates/move-analyzer/editors/code/tests/runTests.ts b/external-crates/move/crates/move-analyzer/editors/code/tests/runTests.ts index a8cd46f28528f..750421cc9844b 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/tests/runTests.ts +++ b/external-crates/move/crates/move-analyzer/editors/code/tests/runTests.ts @@ -43,6 +43,10 @@ async function runVSCodeTest(vscodeVersion: string): Promise { } // Install vscode and depends extension + // + // TODO: currently, running `npm test` fails with an ENOENT error when spawning Electron; + // make sure that the path is correct and that `npm test` runs correctly to completion + const vscodeExecutablePath = await downloadAndUnzipVSCode(vscodeVersion); const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); const newCli = cli ?? 'code'; diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts index 14f4351d06706..311155f5e2874 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts @@ -13,7 +13,14 @@ import { } from '@vscode/debugadapter'; import { DebugProtocol } from '@vscode/debugprotocol'; import * as path from 'path'; -import { Runtime, RuntimeEvents, IRuntimeVariableScope } from './runtime'; +import { + Runtime, + RuntimeEvents, + RuntimeValueType, + IRuntimeVariableScope, + CompoundType +} from './runtime'; +import { run } from 'node:test'; const enum LogLevel { Log = 'log', @@ -80,19 +87,16 @@ export class MoveDebugSession extends LoggingDebugSession { private runtime: Runtime; /** - * Handles to create variable scopes - * (ideally we would use numbers but DAP package does not like it) - * + * Handles to create variable scopes and compound variable values. */ - private variableHandles: Handles; - + private variableHandles: Handles; public constructor() { super(); this.setDebuggerLinesStartAt1(false); this.setDebuggerColumnsStartAt1(false); this.runtime = new Runtime(); - this.variableHandles = new Handles(); + this.variableHandles = new Handles(); // setup event handlers @@ -268,27 +272,66 @@ export class MoveDebugSession extends LoggingDebugSession { this.sendResponse(response); } + /** + * Converts a runtime value to a DAP variable. + * + * @param value variable value + * @param name variable name + * @param type optional variable type + * @returns a DAP variable. + */ + private convertRuntimeValue( + value: RuntimeValueType, + name: string, + type?: string + ): DebugProtocol.Variable { + if (typeof value === 'string') { + return { + name, + type, + value, + variablesReference: 0 + }; + } else if (Array.isArray(value)) { + const compoundValueReference = this.variableHandles.create(value); + return { + name, + type, + value: '(' + value.length + ')[...]', + variablesReference: compoundValueReference + }; + } else { + const compoundValueReference = this.variableHandles.create(value); + const accessChainParts = value.type.split('::'); + const datatypeName = accessChainParts[accessChainParts.length - 1]; + return { + name, + type: value.variantName + ? value.type + '::' + value.variantName + : value.type, + value: (value.variantName + ? datatypeName + '::' + value.variantName + : datatypeName + ) + '{...}', + variablesReference: compoundValueReference + }; + } + } + /** * Converts runtime variables to DAP variables. * * @param runtimeScope runtime variables scope, - * @returns an array of variables. + * @returns an array of DAP variables. */ private convertRuntimeVariables(runtimeScope: IRuntimeVariableScope): DebugProtocol.Variable[] { const variables: DebugProtocol.Variable[] = []; const runtimeVariables = runtimeScope.locals; - for (let i = 0; i < runtimeVariables.length; i++) { - const v = runtimeVariables[i]; + runtimeVariables.forEach(v => { if (v) { - variables.push({ - name: v.name, - type: v.type, - value: v.value, - variablesReference: 0 - }); + variables.push(this.convertRuntimeValue(v.value, v.name, v.type)); } - } - + }); return variables; } @@ -296,13 +339,27 @@ export class MoveDebugSession extends LoggingDebugSession { response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments ): void { - const handle = this.variableHandles.get(args.variablesReference); - if (!handle) { - this.sendResponse(response); - return; - } try { - const variables = this.convertRuntimeVariables(handle); + const variableHandle = this.variableHandles.get(args.variablesReference); + let variables: DebugProtocol.Variable[] = []; + if (variableHandle) { + if ('locals' in variableHandle) { + // we are dealing with a sccope + variables = this.convertRuntimeVariables(variableHandle); + } else { + // we are dealing with a compound value + if (Array.isArray(variableHandle)) { + for (let i = 0; i < variableHandle.length; i++) { + const v = variableHandle[i]; + variables.push(this.convertRuntimeValue(v, String(i))); + } + } else { + variableHandle.fields.forEach(([fname, fvalue]) => { + variables.push(this.convertRuntimeValue(fvalue, fname)); + }); + } + } + } if (variables.length > 0) { response.body = { variables @@ -312,7 +369,6 @@ export class MoveDebugSession extends LoggingDebugSession { response.success = false; response.message = err instanceof Error ? err.message : String(err); } - this.sendResponse(response); } @@ -323,11 +379,7 @@ export class MoveDebugSession extends LoggingDebugSession { ): void { let terminate = false; try { - terminate = this.runtime.step( - /* next */ true, - /* stopAtCloseFrame */ false, - /* nextLineSkip */ true - ); + terminate = this.runtime.step(/* next */ true, /* stopAtCloseFrame */ false); } catch (err) { response.success = false; response.message = err instanceof Error ? err.message : String(err); @@ -344,11 +396,7 @@ export class MoveDebugSession extends LoggingDebugSession { ): void { let terminate = false; try { - terminate = this.runtime.step( - /* next */ false, - /* stopAtCloseFrame */ false, - /* nextLineSkip */ true - ); + terminate = this.runtime.step(/* next */ false, /* stopAtCloseFrame */ false); } catch (err) { response.success = false; response.message = err instanceof Error ? err.message : String(err); @@ -364,7 +412,7 @@ export class MoveDebugSession extends LoggingDebugSession { _args: DebugProtocol.StepOutArguments ): void { try { - const steppedOut = this.runtime.stepOut(); + const steppedOut = this.runtime.stepOut(/* next */ false); if (!steppedOut) { logger.log(`Cannot step out`); } @@ -375,46 +423,13 @@ export class MoveDebugSession extends LoggingDebugSession { this.sendResponse(response); } - protected stepBackRequest( - response: DebugProtocol.StepBackResponse, - _args: DebugProtocol.StepBackArguments - ): void { - try { - const steppedBack = this.runtime.stepBack(); - if (!steppedBack) { - logger.log(`Cannot step back`); - } - } catch (err) { - response.success = false; - response.message = err instanceof Error ? err.message : String(err); - } - this.sendResponse(response); - } - protected continueRequest( response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments ): void { let terminate = false; try { - terminate = this.runtime.continue(/* reverse */ false); - } catch (err) { - response.success = false; - response.message = err instanceof Error ? err.message : String(err); - } - if (terminate) { - this.sendEvent(new TerminatedEvent()); - } - this.sendResponse(response); - } - - protected reverseContinueRequest( - response: DebugProtocol.ReverseContinueResponse, - _args: DebugProtocol.ReverseContinueArguments - ): void { - let terminate = false; - try { - terminate = this.runtime.continue(/* reverse */ true); + terminate = this.runtime.continue(); } catch (err) { response.success = false; response.message = err instanceof Error ? err.message : String(err); diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts index 6739f3247c170..233e561c3fa41 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts @@ -7,7 +7,7 @@ import * as fs from 'fs'; import * as path from 'path'; import toml from 'toml'; import { ISourceMap, IFileInfo, readAllSourceMaps } from './source_map_utils'; -import { TraceEffectKind, TraceEvent, TraceEventKind, TraceLocKind, TraceValKind, TraceValue, readTrace } from './trace_utils'; +import { TraceEffectKind, TraceEvent, TraceEventKind, TraceInstructionKind, TraceLocKind, TraceValKind, TraceValue, readTrace } from './trace_utils'; import { ModuleInfo } from './utils'; /** @@ -18,12 +18,33 @@ export interface IRuntimeVariableScope { locals: (IRuntimeVariable | undefined)[]; } +/** + * A compound type: + * - a vector (converted to an array of values) + * - a struct/enum (converted to an array of string/field value pairs) + */ +export type CompoundType = RuntimeValueType[] | IRuntimeCompundValue; + +/** + * A runtime value can have any of the following types: + * - boolean, number, string (converted to string) + * - compound type (vector, struct, enum) + */ +export type RuntimeValueType = string | CompoundType; + +export interface IRuntimeCompundValue { + fields: [string, RuntimeValueType][]; + type: string; + variantName?: string; + variantTag?: number; +} + /** * Describes a runtime local variable. */ interface IRuntimeVariable { name: string; - value: string; + value: RuntimeValueType; type: string; } @@ -47,6 +68,11 @@ interface IRuntimeStackFrame { // Local variables per scope (local scope at 0 and then following block scopes), // indexed by variable frame index. locals: (IRuntimeVariable | undefined)[][]; + /** + * Line of the last call instruction that was processed in this frame. + * It's needed to make sure that step/next into/over call works correctly. + */ + lastCallInstructionLine: number | undefined; } /** @@ -72,19 +98,29 @@ export enum RuntimeEvents { */ export class Runtime extends EventEmitter { - // Trace being viewed. + /** + * Trace being viewed. + */ private trace = { events: [] as TraceEvent[], localLifetimeEnds: new Map() }; - // Index of the current trace event being processed. + /** + * Index of the current trace event being processed. + */ private eventIndex = 0; - // Current frame stack. + /** + * Current frame stack. + */ private frameStack = { frames: [] as IRuntimeStackFrame[] }; - // Map of file hashes to file info. + /** + * Map of file hashes to file info. + */ private filesMap = new Map(); - // Map of stringified module info to source maps. + /** + * Map of stringified module info to source maps. + */ private sourceMapsMap = new Map(); /** @@ -139,7 +175,7 @@ export class Runtime extends EventEmitter { this.frameStack = { frames: [newFrame] }; - this.step(/* next */ false, /* stopAtCloseFrame */ false, /* nextLineSkip */ true); + this.step(/* next */ false, /* stopAtCloseFrame */ false); } /** @@ -160,11 +196,7 @@ export class Runtime extends EventEmitter { * @returns `true` if the trace viewing session is finished, `false` otherwise. * @throws Error with a descriptive error message if the step event cannot be handled. */ - public step( - next: boolean, - stopAtCloseFrame: boolean, - nextLineSkip: boolean - ): boolean { + public step(next: boolean, stopAtCloseFrame: boolean): boolean { this.eventIndex++; if (this.eventIndex >= this.trace.events.length) { this.sendEvent(RuntimeEvents.stopOnStep); @@ -172,9 +204,85 @@ export class Runtime extends EventEmitter { } let currentEvent = this.trace.events[this.eventIndex]; if (currentEvent.type === TraceEventKind.Instruction) { - let sameLine = this.instruction(currentEvent); - if (sameLine && nextLineSkip) { - return this.step(next, stopAtCloseFrame, nextLineSkip); + const stackHeight = this.frameStack.frames.length; + if (stackHeight <= 0) { + throw new Error('No frame on the stack when processing Instruction event at PC: ' + + currentEvent.pc); + } + const currentFrame = this.frameStack.frames[stackHeight - 1]; + // remember last call instruction line before it (potentially) changes + // in the `instruction` call below + const lastCallInstructionLine = currentFrame.lastCallInstructionLine; + let [sameLine, currentLine] = this.instruction(currentFrame, currentEvent); + if (sameLine) { + if (!next && (currentEvent.kind === TraceInstructionKind.CALL + || currentEvent.kind === TraceInstructionKind.CALL_GENERIC) + && lastCallInstructionLine === currentLine) { + // We are about to step into another call on the same line + // but we should wait for user action to do so rather than + // having debugger step into it automatically. If we don't + // the user will observe a weird effect. For example, + // consider the following code: + // ``` + // foo(); + // assert(bar() == baz()); + // ``` + // In the code above, after executing `foo()`, the user + // will move to the next line and will expect to only + // step into `bar` rather than having debugger to step + // immediately into `baz` as well. At the same time, + // if the user intended to step over functions using `next`, + // we shuld skip over all calls on the same line (both `bar` + // and `baz` in the example above). + // + // The following explains a bit more formally what needs + // to happen both on on `next` and `step` actions when + // call and non-call instructions are interleaved: + // + // When `step` is called: + // + // When there is only one call on the same line, we want to + // stop on the first instruction of this line, then after + // user `step` action enter the call, and then after + // exiting the call go to the instruction on the next line: + // 6: instruction + // 7: instruction // stop here + // 7: call // enter call here + // 7: instruction + // 8: instruction // stop here + // + // When there is more than one call on the same line, we + // want to stop on the first instruction of this line, + // then after user `step` action enter the call, then + // after exiting the call stop on the next call instruction + // and waitl for another `step` action from the user: + // 6: instruction + // 7: instruction // stop here + // 7: call // enter call here + // 7: instruction + // 7: call // stop and then enter call here + // 7: instruction + // 8: instruction // stop here + // + // When `next` is called, things have to happen differently, + // particularly when there are multiple calls on the same line: + // 6: instruction + // 7: instruction // stop here + // 7: call + // 7: instruction + // 7: call + // 7: instruction + // 8: instruction // stop here + // + // To support this, we need to keep track of the line number when + // the last call instruction in a give frame happened, and + // also we need to make `stepOut` aware of whether it is executed + // as part of `next` (which is how `next` is implemented) or not. + this.sendEvent(RuntimeEvents.stopOnStep); + return false; + } else { + return this.step(next, stopAtCloseFrame); + } } this.sendEvent(RuntimeEvents.stopOnStep); return false; @@ -195,10 +303,10 @@ export class Runtime extends EventEmitter { if (next) { // step out of the frame right away - this.stepOut(); + this.stepOut(next); return false; } else { - return this.step(next, stopAtCloseFrame, nextLineSkip); + return this.step(next, stopAtCloseFrame); } } else if (currentEvent.type === TraceEventKind.CloseFrame) { if (stopAtCloseFrame) { @@ -212,7 +320,7 @@ export class Runtime extends EventEmitter { + currentEvent.id); } this.frameStack.frames.pop(); - return this.step(next, stopAtCloseFrame, nextLineSkip); + return this.step(next, stopAtCloseFrame); } } else if (currentEvent.type === TraceEventKind.Effect) { const effect = currentEvent.effect; @@ -228,20 +336,21 @@ export class Runtime extends EventEmitter { localWrite(currentFrame, traceLocation.localIndex, traceValue); } } - return this.step(next, stopAtCloseFrame, nextLineSkip); + return this.step(next, stopAtCloseFrame); } else { // ignore other events - return this.step(next, stopAtCloseFrame, nextLineSkip); + return this.step(next, stopAtCloseFrame); } } /** * Handles "step out" adapter action. * + * @param next determines if it's part of `next` (or otherwise `step`) action. * @returns `true` if was able to step out of the frame, `false` otherwise. * @throws Error with a descriptive error message if the step out event cannot be handled. */ - public stepOut(): boolean { + public stepOut(next: boolean): boolean { const stackHeight = this.frameStack.frames.length; if (stackHeight <= 1) { // do nothing as there is no frame to step out to @@ -254,7 +363,11 @@ export class Runtime extends EventEmitter { // skip all events until the corresponding CloseFrame event, // pop the top frame from the stack, and proceed to the next event while (true) { - if (this.step(/* next */ false, /* stopAtCloseFrame */ true, /* nextLineSkip */ true)) { + // when calling `step` in the loop below, we need to avoid + // skipping over calls next-style otherwise we can miss seeing + // the actual close frame event that we are looking for + // and have the loop execute too far + if (this.step(/* next */ false, /* stopAtCloseFrame */ true)) { // trace viewing session finished throw new Error('Cannot find corresponding CloseFrame event for function: ' + currentFrame.name); @@ -270,103 +383,7 @@ export class Runtime extends EventEmitter { } } } - - // Do not skip to same line when stepping out as this may lead - // to unusual behavior if multiple bytcode instructions are on the same line. - // For example, consider the following code: - // ``` - // assert(foo() == bar()); - // ``` - // In the code above if we enter `foo` and then step out of it, - // we want to end up on the same line (where the next instruction is) - // but we don't want to call `bar` in the same debugging step. - return this.step(/* next */ false, /* stopAtCloseFrame */ false, /* nextLineSkip */ false); - } - /** - * Handles "step back" adapter action. - * @returns `true` if was able to step back, `false` otherwise. - * @throws Error with a descriptive error message if the step back event cannot be handled. - */ - public stepBack(): boolean { - if (this.eventIndex <= 1) { - // no where to step back to (event 0 is the `OpenFrame` event for the first frame) - // and is processed in runtime.start() which is executed only once - this.sendEvent(RuntimeEvents.stopOnStep); - return false; - } - let currentEvent = this.trace.events[this.eventIndex - 1]; - if (currentEvent.type === TraceEventKind.CloseFrame) { - // cannot step back into or over function calls - this.sendEvent(RuntimeEvents.stopOnStep); - return false; - } else { - this.eventIndex--; - if (currentEvent.type === TraceEventKind.Instruction) { - let sameLine = this.instruction(currentEvent); - if (sameLine) { - this.stepBack(); - return true; - } - this.sendEvent(RuntimeEvents.stopOnStep); - return true; - } else if (currentEvent.type === TraceEventKind.OpenFrame) { - const stackHeight = this.frameStack.frames.length; - if (stackHeight <= 0) { - // should never happen but better to signal than crash - throw new Error('Error stepping back to caller function ' - + currentEvent.name - + ' as there is no frame on the stack' - ); - } - if (stackHeight <= 1) { - // should never happen as we never step back out of the outermost function - // (never step back to event 0 as per first conditional in this function) - throw new Error('Error stepping back to caller function ' - + currentEvent.name - + ' from callee ' - + this.frameStack.frames[stackHeight - 1].name - + ' as there would be no frame on the stack afterwards' - ); - } - // pop the top frame from the stack - this.frameStack.frames.pop(); - // cannot simply call stepBack as we are stepping back to the same line - // that is now in the current frame, which would result in unintentionally - // recursing to previous events - if (this.eventIndex <= 1) { - // no where to step back to - this.sendEvent(RuntimeEvents.stopOnStep); - return true; // we actually stepped back just can't step back further - } - this.eventIndex--; - let prevCurrentEvent = this.trace.events[this.eventIndex]; - if (prevCurrentEvent.type !== TraceEventKind.Instruction) { - throw new Error('Expected an Instruction event before OpenFrame event in function' - + currentEvent.name - ); - } - if (!this.instruction(prevCurrentEvent)) { - // we should be steppping back to the instruction on the same line - // as the one in the current frame - throw new Error('Wrong line of an instruction (at PC ' + prevCurrentEvent.pc + ')' - + ' in the caller function ' - + currentEvent.name - + ' to step back to from callee ' - + this.frameStack.frames[stackHeight - 1].name - + ' as there would be no frame on the stack afterwards' - ); - } - this.sendEvent(RuntimeEvents.stopOnStep); - return true; - } else if (currentEvent.type === TraceEventKind.Effect) { - // TODO: implement reverting writes when stepping back - return this.stepBack(); - } else { - // ignore other events - this.stepBack(); - return true; - } - } + return this.step(next, /* stopAtCloseFrame */ false); } /** @@ -374,25 +391,12 @@ export class Runtime extends EventEmitter { * @returns `true` if the trace viewing session is finished, `false` otherwise. * @throws Error with a descriptive error message if the continue event cannot be handled. */ - public continue(reverse: boolean): boolean { - if (reverse) { - while (true) { - if (!this.stepBack()) { - return false; - } - } - } else { - while (true) { - if (this.step( - /* next */ false, - /* stopAtCloseFrame */ false, - /* nextLineSkip */ true) - ) { - return true; - } + public continue(): boolean { + while (true) { + if (this.step(/* next */ false, /* stopAtCloseFrame */ false)) { + return true; } } - } /** @@ -403,14 +407,10 @@ export class Runtime extends EventEmitter { * `false` otherwise (so that instructions on the same line can be skipped). * @throws Error with a descriptive error message if instruction event cannot be handled. */ - private instruction(instructionEvent: Extract): boolean { - const stackHeight = this.frameStack.frames.length; - if (stackHeight <= 0) { - throw new Error('No frame on the stack when processing Instruction event at PC: ' - + instructionEvent.pc); - } - // newest frame is at the top of the stack - const currentFrame = this.frameStack.frames[stackHeight - 1]; + private instruction( + currentFrame: IRuntimeStackFrame, + instructionEvent: Extract + ): [boolean, number] { const currentFun = currentFrame.sourceMap.functions.get(currentFrame.name); if (!currentFun) { throw new Error(`Cannot find function: ${currentFrame.name} in source map`); @@ -452,12 +452,17 @@ export class Runtime extends EventEmitter { } } + if (instructionEvent.kind === TraceInstructionKind.CALL || + instructionEvent.kind === TraceInstructionKind.CALL_GENERIC) { + currentFrame.lastCallInstructionLine = currentPCLoc.line; + } + if (currentPCLoc.line === currentFrame.line) { // so that instructions on the same line can be bypassed - return true; + return [true, currentPCLoc.line]; } else { currentFrame.line = currentPCLoc.line; - return false; + return [false, currentPCLoc.line]; } } @@ -502,7 +507,8 @@ export class Runtime extends EventEmitter { file: currentFile.path, line: 0, // line will be updated when next event (Instruction) is processed localsTypes, - locals + locals, + lastCallInstructionLine: undefined, }; if (this.trace.events.length <= this.eventIndex + 1 || diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/source_map_utils.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/source_map_utils.ts index 978f28e81a444..23edbd8424175 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/source_map_utils.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/source_map_utils.ts @@ -7,40 +7,40 @@ import { ModuleInfo } from './utils'; // Data types corresponding to source map file JSON schema. -interface ISrcDefinitionLocation { +interface JSONSrcDefinitionLocation { file_hash: number[]; start: number; end: number; } -interface ISrcStructSourceMapEntry { - definition_location: ISrcDefinitionLocation; - type_parameters: [string, ISrcDefinitionLocation][]; - fields: ISrcDefinitionLocation[]; +interface JSONSrcStructSourceMapEntry { + definition_location: JSONSrcDefinitionLocation; + type_parameters: [string, JSONSrcDefinitionLocation][]; + fields: JSONSrcDefinitionLocation[]; } -interface ISrcEnumSourceMapEntry { - definition_location: ISrcDefinitionLocation; - type_parameters: [string, ISrcDefinitionLocation][]; - variants: [[string, ISrcDefinitionLocation], ISrcDefinitionLocation[]][]; +interface JSONSrcEnumSourceMapEntry { + definition_location: JSONSrcDefinitionLocation; + type_parameters: [string, JSONSrcDefinitionLocation][]; + variants: [[string, JSONSrcDefinitionLocation], JSONSrcDefinitionLocation[]][]; } -interface ISrcFunctionMapEntry { - definition_location: ISrcDefinitionLocation; - type_parameters: [string, ISrcDefinitionLocation][]; - parameters: [string, ISrcDefinitionLocation][]; - locals: [string, ISrcDefinitionLocation][]; +interface JSONSrcFunctionMapEntry { + definition_location: JSONSrcDefinitionLocation; + type_parameters: [string, JSONSrcDefinitionLocation][]; + parameters: [string, JSONSrcDefinitionLocation][]; + locals: [string, JSONSrcDefinitionLocation][]; nops: Record; - code_map: Record; + code_map: Record; is_native: boolean; } -interface ISrcRootObject { - definition_location: ISrcDefinitionLocation; +interface JSONSrcRootObject { + definition_location: JSONSrcDefinitionLocation; module_name: string[]; - struct_map: Record; - enum_map: Record; - function_map: Record; + struct_map: Record; + enum_map: Record; + function_map: Record; constant_map: Record; } @@ -126,7 +126,7 @@ export function readAllSourceMaps( * @throws Error if with a descriptive error message if the source map cannot be read. */ function readSourceMap(sourceMapPath: string, filesMap: Map): ISourceMap { - const sourceMapJSON: ISrcRootObject = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8')); + const sourceMapJSON: JSONSrcRootObject = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8')); const fileHash = Buffer.from(sourceMapJSON.definition_location.file_hash).toString('base64'); const modInfo: ModuleInfo = { @@ -212,7 +212,7 @@ function readSourceMap(sourceMapPath: string, filesMap: Map): * @param sourceMapLines */ function prePopulateSourceMapLines( - sourceMapJSON: ISrcRootObject, + sourceMapJSON: JSONSrcRootObject, fileInfo: IFileInfo, sourceMapLines: Set ): void { @@ -265,7 +265,7 @@ function prePopulateSourceMapLines( * @param sourceMapLines set of source file lines. */ function addLinesForLocation( - loc: ISrcDefinitionLocation, + loc: JSONSrcDefinitionLocation, fileInfo: IFileInfo, sourceMapLines: Set ): void { diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts index 3f9e576210600..b4f48e9d9b25a 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts @@ -3,117 +3,172 @@ import * as fs from 'fs'; import { FRAME_LIFETIME, ModuleInfo } from './utils'; +import { IRuntimeCompundValue, RuntimeValueType } from './runtime'; + // Data types corresponding to trace file JSON schema. -interface ITraceModule { +interface JSONTraceModule { + address: string; + name: string; +} + +interface JSONStructTypeDescription { address: string; + module: string; name: string; + type_args: string[]; +} + +interface JSONStructType { + struct: JSONStructTypeDescription; +} + +interface JSONVectorType { + vector: JSONBaseType; } -interface ITraceType { +type JSONBaseType = string | JSONStructType | JSONVectorType; + +interface JSONTraceType { ref_type: string | null; - type_: string; + type_: JSONBaseType; +} + +type JSONTraceValueType = boolean | number | string | JSONTraceValueType[] | JSONTraceCompound; + +interface JSONTraceFields { + [key: string]: JSONTraceValueType; +} + +interface JSONTraceCompound { + fields: JSONTraceFields; + type: string; + variant_name?: string; + variant_tag?: number; } -interface ITraceRuntimeValue { - value: any; +interface JSONTraceRuntimeValue { + value: JSONTraceValueType; } -interface ITraceValue { - RuntimeValue: ITraceRuntimeValue; +interface JSONTraceValue { + RuntimeValue: JSONTraceRuntimeValue; } -interface ITraceFrame { +interface JSONTraceFrame { binary_member_index: number; frame_id: number; function_name: string; is_native: boolean; - locals_types: ITraceType[]; - module: ITraceModule; - parameters: ITraceValue[]; - return_types: ITraceType[]; + locals_types: JSONTraceType[]; + module: JSONTraceModule; + parameters: JSONTraceValue[]; + return_types: JSONTraceType[]; type_instantiation: string[]; } -interface ITraceOpenFrame { - frame: ITraceFrame; +interface JSONTraceOpenFrame { + frame: JSONTraceFrame; gas_left: number; } -interface ITraceInstruction { +interface JSONTraceInstruction { gas_left: number; instruction: string; pc: number; type_parameters: any[]; } -interface ITraceLocation { +interface JSONTraceLocalLocation { Local: [number, number]; } -interface ITraceWriteEffect { - location: ITraceLocation; - root_value_after_write: ITraceValue; +interface JSONTraceIndexedLocation { + Indexed: [JSONTraceLocalLocation, number]; +} + +type JSONTraceLocation = JSONTraceLocalLocation | JSONTraceIndexedLocation; + +interface JSONTraceWriteEffect { + location: JSONTraceLocation; + root_value_after_write: JSONTraceValue; } -interface ITraceReadEffect { - location: ITraceLocation; +interface JSONTraceReadEffect { + location: JSONTraceLocation; moved: boolean; - root_value_read: ITraceValue; + root_value_read: JSONTraceValue; } -interface ITracePushEffect { - RuntimeValue?: ITraceRuntimeValue; +interface JSONTracePushEffect { + RuntimeValue?: JSONTraceRuntimeValue; MutRef?: { - location: ITraceLocation; + location: JSONTraceLocation; snapshot: any[]; }; } -interface ITracePopEffect { - RuntimeValue?: ITraceRuntimeValue; +interface JSONTracePopEffect { + RuntimeValue?: JSONTraceRuntimeValue; MutRef?: { - location: ITraceLocation; + location: JSONTraceLocation; snapshot: any[]; }; } -interface ITraceEffect { - Push?: ITracePushEffect; - Pop?: ITracePopEffect; - Write?: ITraceWriteEffect; - Read?: ITraceReadEffect; +interface JSONTraceEffect { + Push?: JSONTracePushEffect; + Pop?: JSONTracePopEffect; + Write?: JSONTraceWriteEffect; + Read?: JSONTraceReadEffect; } -interface ITraceCloseFrame { +interface JSONTraceCloseFrame { frame_id: number; gas_left: number; - return_: ITraceRuntimeValue[]; + return_: JSONTraceRuntimeValue[]; } -interface ITraceEvent { - OpenFrame?: ITraceOpenFrame; - Instruction?: ITraceInstruction; - Effect?: ITraceEffect; - CloseFrame?: ITraceCloseFrame; +interface JSONTraceEvent { + OpenFrame?: JSONTraceOpenFrame; + Instruction?: JSONTraceInstruction; + Effect?: JSONTraceEffect; + CloseFrame?: JSONTraceCloseFrame; } -interface ITraceRootObject { - events: ITraceEvent[]; +interface JSONTraceRootObject { + events: JSONTraceEvent[]; version: number; } // Runtime data types. +/** + * Kind of instruction in the trace. Enum member names correspond to instruction names. + * (other than UNKNOWN which is used for instructions whose kind does not matter). + */ +export enum TraceInstructionKind { + /** + * Call instruction. + */ + CALL, + /** + * Generic call instruction. + */ + CALL_GENERIC, + // for now we don't care about other kinds of instructions + UNKNOWN +} + /** * Kind of a trace event. */ export enum TraceEventKind { - OpenFrame = 'OpenFrame', - CloseFrame = 'CloseFrame', - Instruction = 'Instruction', - Effect = 'Effect' + OpenFrame, + CloseFrame, + Instruction, + Effect } /** @@ -129,7 +184,7 @@ export type TraceEvent = paramValues: TraceValue[] } | { type: TraceEventKind.CloseFrame, id: number } - | { type: TraceEventKind.Instruction, pc: number } + | { type: TraceEventKind.Instruction, pc: number, kind: TraceInstructionKind } | { type: TraceEventKind.Effect, effect: EventEffect }; /** @@ -158,7 +213,7 @@ export enum TraceValKind { * Value in the trace. */ export type TraceValue = - | { type: TraceValKind.Runtime, value: string }; + | { type: TraceValKind.Runtime, value: RuntimeValueType }; /** * Kind of an effect of an instruction. @@ -188,7 +243,6 @@ interface ITrace { localLifetimeEnds: Map; } - /** * Reads a Move VM execution trace from a JSON file. * @@ -196,7 +250,7 @@ interface ITrace { * @returns execution trace. */ export function readTrace(traceFilePath: string): ITrace { - const traceJSON: ITraceRootObject = JSON.parse(fs.readFileSync(traceFilePath, 'utf8')); + const traceJSON: JSONTraceRootObject = JSON.parse(fs.readFileSync(traceFilePath, 'utf8')); const events: TraceEvent[] = []; // We compute the end of lifetime for a local variable as follows. // When a given local variable is read or written in an effect, we set the end of its lifetime @@ -224,7 +278,7 @@ export function readTrace(traceFilePath: string): ITrace { const localsTypes = []; const frame = event.OpenFrame.frame; for (const type of frame.locals_types) { - localsTypes.push(type.type_); + localsTypes.push(JSONTraceTypeToString(type.type_)); } // process parameters - store their values in trace and set their // initial lifetimes @@ -234,7 +288,10 @@ export function readTrace(traceFilePath: string): ITrace { const value = frame.parameters[i]; if (value) { const runtimeValue: TraceValue = - { type: TraceValKind.Runtime, value: JSON.stringify(value.RuntimeValue.value) }; + { + type: TraceValKind.Runtime, + value: traceValueFromJSON(value.RuntimeValue.value) + }; paramValues.push(runtimeValue); lifetimeEnds[i] = FRAME_LIFETIME; } @@ -259,9 +316,13 @@ export function readTrace(traceFilePath: string): ITrace { }); frameIDs.pop(); } else if (event.Instruction) { + const name = event.Instruction.instruction; events.push({ type: TraceEventKind.Instruction, - pc: event.Instruction.pc + pc: event.Instruction.pc, + kind: name in TraceInstructionKind + ? TraceInstructionKind[name as keyof typeof TraceInstructionKind] + : TraceInstructionKind.UNKNOWN }); // Set end of lifetime for all locals to the max instruction PC ever seen // for a given local (if they are live after this instructions, they will @@ -288,36 +349,117 @@ export function readTrace(traceFilePath: string): ITrace { // if a local is read or written, set its end of lifetime // to infinite (end of frame) const location = effect.Write ? effect.Write.location : effect.Read!.location; - const frameId = location.Local[0]; - const localIndex = location.Local[1]; - const lifetimeEnds = localLifetimeEnds.get(frameId) || []; - lifetimeEnds[localIndex] = FRAME_LIFETIME; - localLifetimeEnds.set(frameId, lifetimeEnds); - + // there must be at least one frame on the stack when processing a write effect + // so we can safely access the last frame ID + const currentFrameID = frameIDs[frameIDs.length - 1]; + const localIndex = processJSONLocation(location, localLifetimeEnds, currentFrameID); + if (localIndex === undefined) { + continue; + } if (effect.Write) { - const value = JSON.stringify(effect.Write.root_value_after_write.RuntimeValue.value); + const value = traceValueFromJSON(effect.Write.root_value_after_write.RuntimeValue.value); const traceValue: TraceValue = { type: TraceValKind.Runtime, value }; - const TraceLocation: TraceLocation = { + const traceLocation: TraceLocation = { type: TraceLocKind.Local, - frameId, + frameId: currentFrameID, localIndex }; events.push({ type: TraceEventKind.Effect, effect: { type: TraceEffectKind.Write, - location: TraceLocation, + location: traceLocation, value: traceValue } }); } } + } + } + return { events, localLifetimeEnds }; +} + +/** + * Converts a JSON trace type to a string representation. + */ +function JSONTraceTypeToString(type: JSONBaseType): string { + if (typeof type === 'string') { + return type; + } else if ('vector' in type) { + return `vector<${JSONTraceTypeToString(type.vector)}>`; + } else { + return JSONTraceAddressToHexString(type.struct.address) + + "::" + + type.struct.module + + "::" + + type.struct.name; + } +} +/** + * Attempts to convert an address found in the trace (which is a string + * representing a 32-byte number) to a shorter and more readable hex string. + * Returns original string address if conversion fails. + */ +function JSONTraceAddressToHexString(address: string): string { + try { + const number = BigInt(address); + const hexAddress = number.toString(16); + return `0x${hexAddress}`; + } catch (error) { + // Return the original string if it's not a valid number + return address; + } +} +/// Processes a location in a JSON trace (sets the end of lifetime for a local variable) +/// and returns the local index if the location is a local variable in the current frame. +function processJSONLocation( + location: JSONTraceLocation, + localLifetimeEnds: Map, + currentFrameID: number +): number | undefined { + // TODO: handle Global and Indexed for other frames + if ('Local' in location) { + const frameId = location.Local[0]; + const localIndex = location.Local[1]; + const lifetimeEnds = localLifetimeEnds.get(frameId) || []; + lifetimeEnds[localIndex] = FRAME_LIFETIME; + localLifetimeEnds.set(frameId, lifetimeEnds); + return localIndex; + } else if ('Indexed' in location) { + const frameId = location.Indexed[0].Local[0]; + if (frameId === currentFrameID) { + const localIndex = location.Indexed[0].Local[1]; + const lifetimeEnds = localLifetimeEnds.get(frameId) || []; + lifetimeEnds[localIndex] = FRAME_LIFETIME; + localLifetimeEnds.set(frameId, lifetimeEnds); + return localIndex; } } - return { events, localLifetimeEnds }; + return undefined; +} + +/// Converts a JSON trace value to a runtime trace value. +function traceValueFromJSON(value: JSONTraceValueType): RuntimeValueType { + if (typeof value === 'boolean' + || typeof value === 'number' + || typeof value === 'string') { + return String(value); + } else if (Array.isArray(value)) { + return value.map(item => traceValueFromJSON(item)); + } else { + const fields: [string, RuntimeValueType][] = + Object.entries(value.fields).map(([key, value]) => [key, traceValueFromJSON(value)]); + const compoundValue: IRuntimeCompundValue = { + fields, + type: value.type, + variantName: value.variant_name, + variantTag: value.variant_tag + }; + return compoundValue; + } } diff --git a/external-crates/move/crates/move-binary-format/src/compatibility.rs b/external-crates/move/crates/move-binary-format/src/compatibility.rs index 0b4a7054d90da..47f2918e1f8a8 100644 --- a/external-crates/move/crates/move-binary-format/src/compatibility.rs +++ b/external-crates/move/crates/move-binary-format/src/compatibility.rs @@ -2,24 +2,21 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -use std::collections::BTreeSet; - use crate::{ compatibility_mode::{CompatibilityMode, ExecutionCompatibilityMode}, errors::{PartialVMError, PartialVMResult}, - file_format::{AbilitySet, DatatypeTyParameter, Visibility}, + file_format::{Ability, AbilitySet, DatatypeTyParameter, Visibility}, file_format_common::VERSION_5, normalized::Module, }; use move_core_types::vm_status::StatusCode; - // *************************************************************************** // ******************* IMPORTANT NOTE ON COMPATIBILITY *********************** // *************************************************************************** // -// If `check_datatype_layout` and/or `check_datatype_and_pub_function_linking` is false, type -// safety over a series of upgrades cannot be guaranteed for either structs or enums. This is -// because the type could first be removed, and then re-introduced with a diferent layout and/or +// If `check_datatype_layout` is false, type safety over a series of upgrades cannot be guaranteed +// for either structs or enums. +// This is because the type could first be removed, and then re-introduced with a diferent layout and/or // additional variants in a later upgrade. E.g., // * For enums you could add a new variant even if `disallow_new_variants` is true, by first // removing the enum in an upgrade, and then reintroducing it with a new variant in a later @@ -31,41 +28,26 @@ use move_core_types::vm_status::StatusCode; /// The result of a linking and layout compatibility check. /// /// Here is what the different combinations of the compatibility flags mean: -/// `{ check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: true }`: fully backward compatible -/// `{ check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: true, check_private_entry_linking: false }`: Backwards compatible, private entry function signatures can change -/// `{ check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: true }`: Backward compatible, exclude the friend module declare and friend functions -/// `{ check_datatype_and_pub_function_linking: true, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: false }`: Backward compatible, exclude the friend module declarations, friend functions, and private and friend entry function -/// `{ check_datatype_and_pub_function_linking: false, check_datatype_layout: true, check_friend_linking: false, check_private_entry_linking: _ }`: Dependent modules that reference functions or types in this module may not link. However, fixing, recompiling, and redeploying all dependent modules will work--no data migration needed. -/// `{ check_datatype_and_pub_function_linking: true, check_datatype_layout: false, check_friend_linking: true, check_private_entry_linking: _ }`: Attempting to read structs published by this module will now fail at runtime. However, dependent modules will continue to link. Requires data migration, but no changes to dependent modules. -/// `{ check_datatype_and_pub_function_linking: false, check_datatype_layout: false, check_friend_linking: false, check_private_entry_linking: _ }`: Everything is broken. Need both a data migration and changes to dependent modules. +/// `{ check_datatype_layout: true, check_private_entry_linking: true }`: fully backward compatible +/// `{ check_datatype_layout: true, check_private_entry_linking: false }`: Backwards compatible, private entry function signatures can change +/// `{ check_datatype_layout: true, check_private_entry_linking: true }`: Backward compatible, exclude the friend module declare and friend functions +/// `{ check_datatype_layout: true, check_private_entry_linking: false }`: Backward compatible, exclude the friend module declarations, friend functions, and private and friend entry function #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct Compatibility { - /// if false, do not ensure the dependent modules that reference public functions or structs in this module can link - pub check_datatype_and_pub_function_linking: bool, /// if false, do not ensure the struct layout capability pub check_datatype_layout: bool, - /// if false, treat `friend` as `private` when `check_datatype_and_pub_function_linking`. - pub check_friend_linking: bool, - /// if false, treat `entry` as `private` when `check_datatype_and_pub_function_linking`. + /// if false, treat `entry` as `private` pub check_private_entry_linking: bool, /// The set of abilities that cannot be added to an already exisiting type. pub disallowed_new_abilities: AbilitySet, - /// Don't allow generic type parameters in structs to change their abilities or constraints. - pub disallow_change_datatype_type_params: bool, - /// Don't allow adding new variants at the end of an enum. - pub disallow_new_variants: bool, } impl Default for Compatibility { fn default() -> Self { Self { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, } } } @@ -77,26 +59,35 @@ impl Compatibility { pub fn no_check() -> Self { Self { - check_datatype_and_pub_function_linking: false, check_datatype_layout: false, - check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } } + /// Check compatibility for userspace module upgrades pub fn upgrade_check() -> Self { Self { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - // We disallow adding new variants to enums for now - disallow_new_variants: true, + } + } + + /// Check compatibility for system module upgrades + pub fn framework_upgrade_check() -> Self { + Self { + check_datatype_layout: true, + // Checking `entry` linkage is required because system packages are updated in-place, and a + // transaction that was rolled back to make way for reconfiguration should still be runnable + // after a reconfiguration that upgraded the framework. + // + // A transaction that calls a system function that was previously `entry` and is now private + // will fail because its entrypoint became no longer callable. A transaction that calls a + // system function that was previously `public entry` and is now just `public` could also + // fail if one of its mutable inputs was being used in another private `entry` function. + check_private_entry_linking: true, + disallowed_new_abilities: AbilitySet::singleton(Ability::Key), } } @@ -146,7 +137,7 @@ impl Compatibility { } if !datatype_type_parameters_compatible( - self.disallow_change_datatype_type_params, + self.check_datatype_layout, &old_struct.type_parameters, &new_struct.type_parameters, ) { @@ -183,7 +174,7 @@ impl Compatibility { } if !datatype_type_parameters_compatible( - self.disallow_change_datatype_type_params, + self.check_datatype_layout, &old_enum.type_parameters, &new_enum.type_parameters, ) { @@ -227,19 +218,10 @@ impl Compatibility { // (i.e. we cannot remove or change public functions) // - old module's script functions are a subset of the new module's script functions // (i.e. we cannot remove or change script functions) - // - for any friend function that is removed or changed in the old module - // - if the function visibility is upgraded to public, it is OK - // - otherwise, it is considered as incompatible. - // - // NOTE: it is possible to relax the compatibility checking for a friend function, i.e., - // we can remove/change a friend function if the function is not used by any module in the - // friend list. But for simplicity, we decided to go to the more restrictive form now and - // we may revisit this in the future. for (name, old_func) in &old_module.functions { + // Check for removed public functions let Some(new_func) = new_module.functions.get(name) else { - if old_func.visibility == Visibility::Friend { - context.function_missing_friend(name, old_func); - } else if old_func.visibility != Visibility::Private { + if old_func.visibility == Visibility::Public { context.function_missing_public(name, old_func); } else if old_func.is_entry && self.check_private_entry_linking { // This must be a private entry function. So set the link breakage if we're @@ -250,14 +232,10 @@ impl Compatibility { }; // Check visibility compatibility - match (old_func.visibility, new_func.visibility) { - (Visibility::Public, Visibility::Private | Visibility::Friend) => { - context.function_lost_public_visibility(name, old_func); - } - (Visibility::Friend, Visibility::Private) => { - context.function_lost_friend_visibility(name, old_func); - } - _ => (), + if old_func.visibility == Visibility::Public + && new_func.visibility != Visibility::Public + { + context.function_lost_public_visibility(name, old_func); } // Check entry compatibility @@ -284,17 +262,6 @@ impl Compatibility { } } - // check friend declarations compatibility - // - // - additions to the list are allowed - // - removals are not allowed - // - let old_friend_module_ids: BTreeSet<_> = old_module.friends.iter().cloned().collect(); - let new_friend_module_ids: BTreeSet<_> = new_module.friends.iter().cloned().collect(); - if !old_friend_module_ids.is_subset(&new_friend_module_ids) { - context.friend_module_missing(old_friend_module_ids, new_friend_module_ids); - } - context.finish(self) } } diff --git a/external-crates/move/crates/move-binary-format/src/compatibility_mode.rs b/external-crates/move/crates/move-binary-format/src/compatibility_mode.rs index aed0eb81546fa..a0848682ae405 100644 --- a/external-crates/move/crates/move-binary-format/src/compatibility_mode.rs +++ b/external-crates/move/crates/move-binary-format/src/compatibility_mode.rs @@ -3,8 +3,6 @@ use crate::file_format::Visibility; use crate::normalized::{Enum, Function, Struct}; use move_core_types::account_address::AccountAddress; use move_core_types::identifier::{IdentStr, Identifier}; -use move_core_types::language_storage::ModuleId; -use std::collections::BTreeSet; /// A trait which will allow accumulating the information necessary for checking upgrade compatibility between two modules, /// while allowing flexibility in the error type that is returned. @@ -79,9 +77,6 @@ pub trait CompatibilityMode: Default { /// Function missing public error occurs when a public function is present in the old module but not in the new module. fn function_missing_public(&mut self, name: &Identifier, old_func: &Function); - /// Function missing friend error occurs when a friend function is present in the old module but not in the new module. - fn function_missing_friend(&mut self, name: &Identifier, old_funcs: &Function); - /// Function missing entry error occurs when an entry function is present in the old module but not in the new module. fn function_missing_entry(&mut self, name: &Identifier, old_func: &Function); @@ -96,9 +91,6 @@ pub trait CompatibilityMode: Default { /// Function lost public visibility error occurs when a function loses its public visibility. fn function_lost_public_visibility(&mut self, name: &Identifier, old_func: &Function); - /// Function lost friend visibility error occurs when a function loses its friend visibility. - fn function_lost_friend_visibility(&mut self, name: &Identifier, old_func: &Function); - /// Function entry compatibility error occurs when an entry function is not compatible. fn function_entry_compatibility( &mut self, @@ -107,14 +99,6 @@ pub trait CompatibilityMode: Default { new_func: &Function, ); - /// Friend module missing error occurs when a friend module is present in the old module but not in the new module. - /// This is a check that is done for `module.friends` old and new id lists. - fn friend_module_missing( - &mut self, - old_friend_module_ids: BTreeSet, - new_friend_module_ids: BTreeSet, - ); - /// Finish the compatibility check and return the error if one has been accumulated from individual errors. fn finish(&self, _: &Compatibility) -> Result<(), Self::Error>; } @@ -122,9 +106,10 @@ pub trait CompatibilityMode: Default { /// Compatibility mode impl for execution compatibility checks. /// These flags are set when a type safety check is violated. see [`Compatibility`] for more information. pub struct ExecutionCompatibilityMode { + /// This can never be overridden with a flag, and thus has no associated [`Compatibility`] flag. + /// In other words public linking can never be broken. all other flags datatype_and_function_linking: bool, datatype_layout: bool, - friend_linking: bool, entry_linking: bool, no_new_variants: bool, } @@ -134,7 +119,6 @@ impl Default for ExecutionCompatibilityMode { Self { datatype_and_function_linking: true, datatype_layout: true, - friend_linking: true, entry_linking: true, no_new_variants: true, } @@ -223,10 +207,6 @@ impl CompatibilityMode for ExecutionCompatibilityMode { self.datatype_and_function_linking = false; } - fn function_missing_friend(&mut self, _name: &Identifier, _old_func: &Function) { - self.friend_linking = false; - } - fn function_missing_entry(&mut self, _name: &Identifier, _old_func: &Function) { self.entry_linking = false; } @@ -237,14 +217,8 @@ impl CompatibilityMode for ExecutionCompatibilityMode { old_func: &Function, _new_func: &Function, ) { - match old_func.visibility { - Visibility::Friend => { - self.friend_linking = false; - } - Visibility::Public => { - self.datatype_and_function_linking = false; - } - Visibility::Private => (), + if old_func.visibility == Visibility::Public { + self.datatype_and_function_linking = false; } if old_func.is_entry { @@ -256,10 +230,6 @@ impl CompatibilityMode for ExecutionCompatibilityMode { self.datatype_and_function_linking = false; } - fn function_lost_friend_visibility(&mut self, _name: &Identifier, _old_func: &Function) { - self.friend_linking = false; - } - fn function_entry_compatibility( &mut self, _name: &Identifier, @@ -269,31 +239,18 @@ impl CompatibilityMode for ExecutionCompatibilityMode { self.entry_linking = false; } - fn friend_module_missing( - &mut self, - _old_friend_module_ids: BTreeSet, - _new_friend_module_ids: BTreeSet, - ) { - self.friend_linking = false; - } - /// Finish by comparing against the compatibility flags. fn finish(&self, compatability: &Compatibility) -> Result<(), ()> { - if compatability.check_datatype_and_pub_function_linking - && !self.datatype_and_function_linking - { + if !self.datatype_and_function_linking { return Err(()); } if compatability.check_datatype_layout && !self.datatype_layout { return Err(()); } - if compatability.check_friend_linking && !self.friend_linking { - return Err(()); - } if compatability.check_private_entry_linking && !self.entry_linking { return Err(()); } - if compatability.disallow_new_variants && !self.no_new_variants { + if compatability.check_datatype_layout && !self.no_new_variants { return Err(()); } diff --git a/external-crates/move/crates/move-binary-format/src/unit_tests/compatibility_tests.rs b/external-crates/move/crates/move-binary-format/src/unit_tests/compatibility_tests.rs index 0e87b655da204..ad07ebe1efa64 100644 --- a/external-crates/move/crates/move-binary-format/src/unit_tests/compatibility_tests.rs +++ b/external-crates/move/crates/move-binary-format/src/unit_tests/compatibility_tests.rs @@ -623,26 +623,18 @@ fn private_entry_signature_change_allowed() { // allow updating signatures of private entry functions assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&module, &updated_module) .is_ok()); // allow updating signatures of private entry functions assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&updated_module, &module) .is_ok()); @@ -698,9 +690,6 @@ fn entry_fun_compat_tests() { (&public_entry_fun, &friend_entry_fun), (&public_entry_fun, &friend_fun), (&public_entry_fun, &no_fun), - (&friend_entry_fun, &no_fun), - (&friend_entry_fun, &private_fun), - (&friend_entry_fun, &entry_fun), ]; let invalid_private_entry_breakages = vec![ @@ -725,13 +714,9 @@ fn entry_fun_compat_tests() { // Every valid combo is valid under `check_private_entry_linking = false` for (prev, new) in valid_combos.into_iter() { assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(prev, new) .is_ok()); @@ -745,39 +730,19 @@ fn entry_fun_compat_tests() { // Every valid combo is valid under `check_private_entry_linking = false` for (prev, new) in valid_entry_fun_changes_with_friend_api_breakage.into_iter() { assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(prev, new) .is_ok()); - - assert!(Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: true, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, - } - .check(prev, new) - .is_err()); } for (prev, new) in invalid_combos.into_iter() { assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(prev, new) .is_err()); @@ -797,37 +762,25 @@ fn public_entry_signature_change_disallowed() { .parameters = vec![Type::U64]; assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&module, &updated_module) .is_err()); assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&updated_module, &module) .is_err()); assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&module, &updated_module) .is_err()); @@ -845,49 +798,17 @@ fn friend_entry_signature_change_allowed() { .parameters = vec![Type::U64]; assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&module, &updated_module) .is_ok()); assert!(Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: true, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, - } - .check(&module, &updated_module) - .is_err()); - - assert!(Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - check_private_entry_linking: true, - disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, - } - .check(&module, &updated_module) - .is_err()); - - assert!(Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: true, check_private_entry_linking: true, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, } .check(&module, &updated_module) .is_err()); diff --git a/external-crates/move/crates/move-cli/src/sandbox/utils/mod.rs b/external-crates/move/crates/move-cli/src/sandbox/utils/mod.rs index fa49bc971751c..d89930c2a6513 100644 --- a/external-crates/move/crates/move-cli/src/sandbox/utils/mod.rs +++ b/external-crates/move/crates/move-cli/src/sandbox/utils/mod.rs @@ -163,13 +163,9 @@ pub(crate) fn explain_publish_error( let new_api = normalized::Module::new(module); if (Compatibility { - check_datatype_and_pub_function_linking: false, check_datatype_layout: true, - check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, }) .check(&old_api, &new_api) .is_err() @@ -178,13 +174,9 @@ pub(crate) fn explain_publish_error( // structs of this type. but probably a bad idea println!("Layout API for structs of module {} has changed. Need to do a data migration of published structs", module_id) } else if (Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: false, - check_friend_linking: false, check_private_entry_linking: true, disallowed_new_abilities: AbilitySet::EMPTY, - disallow_change_datatype_type_params: false, - disallow_new_variants: false, }) .check(&old_api, &new_api) .is_err() diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/compatibility_tests.rs b/external-crates/move/crates/move-vm-integration-tests/src/tests/compatibility_tests.rs index 9462ae21ad370..edfecbe94a62b 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/compatibility_tests.rs +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/compatibility_tests.rs @@ -199,10 +199,7 @@ fn test_enum_upgrade_add_variant_at_front() { } ", ); - let mut compat = Compatibility::default(); - assert!(compat.disallow_new_variants); - assert!(compat.check(&old, &new).is_err()); - compat.disallow_new_variants = false; + let compat = Compatibility::default(); assert!(compat.check(&old, &new).is_err()); assert!(InclusionCheck::Equal.check(&old, &new).is_err()); assert!(InclusionCheck::Subset.check(&old, &new).is_err()); @@ -226,12 +223,9 @@ fn test_enum_upgrade_add_variant_at_end() { } ", ); - let mut compat = Compatibility::default(); - assert!(compat.disallow_new_variants); + let compat = Compatibility::default(); assert!(compat.check(&old, &new).is_err()); // Allow adding new variants at the end of the enum - compat.disallow_new_variants = false; - assert!(compat.check(&old, &new).is_ok()); assert!(InclusionCheck::Equal.check(&old, &new).is_err()); // NOTE: We currently restrict all enums (even in subset mode) so that new enum variants are not // allowed. This assertion will fail when we allow new enum variants in subset mode and should diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aba62dbe38aec..04a14ddddf926 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: version: 18.3.3 '@vanilla-extract/vite-plugin': specifier: ^4.0.13 - version: 4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) postcss: specifier: ^8.4.39 version: 8.4.39 @@ -164,10 +164,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) apps/icons: devDependencies: @@ -486,7 +486,7 @@ importers: version: 4.4.4 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) copy-webpack-plugin: specifier: ^11.0.0 version: 11.0.0(webpack@5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1))) @@ -543,7 +543,7 @@ importers: version: 3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3)) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3))) + version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))) ts-loader: specifier: ^9.4.4 version: 9.5.1(typescript@5.5.3)(webpack@5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1))) @@ -558,13 +558,13 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) web-ext: specifier: ^7.6.2 version: 7.6.2 @@ -622,7 +622,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -637,7 +637,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) dapps/kiosk-cli: dependencies: @@ -737,7 +737,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -749,16 +749,16 @@ importers: version: 3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3)) tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3))) + version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))) typescript: specifier: ^5.5.3 version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) dapps/sponsored-transactions: dependencies: @@ -786,7 +786,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -801,7 +801,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) examples/mev_bot: dependencies: @@ -869,7 +869,7 @@ importers: version: 6.1.0(eslint@8.45.0)(typescript@5.5.3) '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) eslint: specifier: ^8.45.0 version: 8.45.0 @@ -887,10 +887,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) examples/trading/api: dependencies: @@ -976,7 +976,7 @@ importers: version: 6.1.0(eslint@8.45.0)(typescript@5.5.3) '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -1003,7 +1003,32 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) + + examples/usdc-transfer-app: + dependencies: + '@mysten/dapp-kit': + specifier: ^0.14.25 + version: 0.14.25(@tanstack/react-query@5.59.0(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.3)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3) + '@mysten/sui': + specifier: ^1.12.0 + version: 1.12.0(typescript@5.5.3) + '@tanstack/react-query': + specifier: ^5.50.1 + version: 5.59.0(react@18.3.1) + parcel: + specifier: ^2.12.0 + version: 2.12.0(@swc/helpers@0.5.5)(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3) + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + devDependencies: + process: + specifier: ^0.11.10 + version: 0.11.10 sdk/bcs: dependencies: @@ -1025,7 +1050,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/build-scripts: dependencies: @@ -1038,7 +1063,7 @@ importers: version: 1.16.3 '@vanilla-extract/esbuild-plugin': specifier: ^2.3.8 - version: 2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(sass@1.77.6)(terser@5.31.1) + version: 2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -1118,7 +1143,7 @@ importers: version: 7.16.0(eslint@9.6.0)(typescript@5.5.3) '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) eslint: specifier: ^9.6.0 version: 9.6.0 @@ -1136,7 +1161,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/create-dapp/templates/react-e2e-counter: dependencies: @@ -1179,7 +1204,7 @@ importers: version: 6.1.0(eslint@8.45.0)(typescript@5.5.3) '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) eslint: specifier: ^8.45.0 version: 8.45.0 @@ -1197,7 +1222,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/dapp-kit: dependencies: @@ -1249,7 +1274,7 @@ importers: version: 10.3.1 '@testing-library/jest-dom': specifier: ^6.4.6 - version: 6.4.6(vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1)) + version: 6.4.6(vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) '@testing-library/react': specifier: ^16.0.0 version: 16.0.0(@testing-library/dom@10.3.1)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1261,10 +1286,10 @@ importers: version: 18.3.3 '@vanilla-extract/esbuild-plugin': specifier: ^2.3.8 - version: 2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(sass@1.77.6)(terser@5.31.1) + version: 2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) '@vanilla-extract/vite-plugin': specifier: ^4.0.13 - version: 4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) jsdom: specifier: ^24.1.0 version: 24.1.0 @@ -1282,10 +1307,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/deepbook: dependencies: @@ -1313,10 +1338,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1350,10 +1375,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1454,7 +1479,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)) + version: 3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -1466,7 +1491,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/graphql-transport: dependencies: @@ -1518,7 +1543,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1546,10 +1571,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1583,7 +1608,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/move-bytecode-template: devDependencies: @@ -1598,7 +1623,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wasm-pack: specifier: ^0.13.0 version: 0.13.0 @@ -1623,7 +1648,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/typescript: dependencies: @@ -1717,10 +1742,10 @@ importers: version: 5.5.3 vite: specifier: ^5.3.3 - version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + version: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1776,7 +1801,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) sdk/zksend: dependencies: @@ -1807,7 +1832,7 @@ importers: version: 5.5.3 vitest: specifier: ^2.0.1 - version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + version: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) packages: @@ -4127,6 +4152,42 @@ packages: '@ledgerhq/logs@6.12.0': resolution: {integrity: sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==} + '@lezer/common@1.2.2': + resolution: {integrity: sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + + '@lmdb/lmdb-darwin-arm64@2.8.5': + resolution: {integrity: sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@2.8.5': + resolution: {integrity: sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-linux-arm64@2.8.5': + resolution: {integrity: sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm@2.8.5': + resolution: {integrity: sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-x64@2.8.5': + resolution: {integrity: sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-win32-x64@2.8.5': + resolution: {integrity: sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ==} + cpu: [x64] + os: [win32] + '@manypkg/cli@0.21.4': resolution: {integrity: sha512-EACxxb+c/t6G0l1FrlyewZeBnyR5V1cLkXjnBfsay5TN1UgbilFpG6POglzn+lVJet9NqnEKe3RLHABzkIDZ0Q==} engines: {node: '>=14.18.0'} @@ -4171,6 +4232,40 @@ packages: '@microsoft/tsdoc@0.14.2': resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + '@mischnic/json-sourcemap@0.1.1': + resolution: {integrity: sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==} + engines: {node: '>=12.0.0'} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + '@mswjs/cookies@1.1.1': resolution: {integrity: sha512-W68qOHEjx1iD+4VjQudlx26CPIoxmIAtK4ZCexU0/UJBG6jYhcuyzKJx+Iw8uhBIGd9eba64XgWVgo20it1qwA==} engines: {node: '>=18'} @@ -4179,6 +4274,25 @@ packages: resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==} engines: {node: '>=18'} + '@mysten/bcs@1.1.0': + resolution: {integrity: sha512-yy9/1Y4d0FlRywS1+9ze/T7refCbrvwFwJIOKs9M3QBK1njbcHZp+LkVeLqBvIJA5eZ3ZCzmhQ1Xq4Sed5mEBA==} + + '@mysten/dapp-kit@0.14.25': + resolution: {integrity: sha512-6efjJgikX09AprXU28srmxkLy8e21YtQRtY23OHGLtP3MUkI71kqt3PhBEK4Tg61e0+YCAY/9UlgVWMBX1Hong==} + peerDependencies: + '@tanstack/react-query': ^5.0.0 + react: '*' + + '@mysten/sui@1.12.0': + resolution: {integrity: sha512-DrSyja04xyGrTGlIQKMwZ6MywxNPkjyIcDLm915Zisoy1/uIgPoHc4cx53JyiG92z/HgowTVGGCCIzH53DIYXA==} + engines: {node: '>=18'} + + '@mysten/wallet-standard@0.13.7': + resolution: {integrity: sha512-FXlqn3Gp4E7aQf33rZQfaCEUeEq9TbmkIFA7kX/Yab5SC5892XVhkLRu040eBs8Cest98jFUZ2ZJ4YWR+a7e5g==} + + '@mysten/zksend@0.11.6': + resolution: {integrity: sha512-dkwPQxzPUK9xDTl2mMa2+VcCQNWtKwiTTr8yq17AYOSIqtmPjoX4F4q+oOunL9vgyiJONSNktO1kq37GSjIa6w==} + '@nanostores/react@0.7.2': resolution: {integrity: sha512-e3OhHJFv3NMSFYDgREdlAQqkyBTHJM91s31kOZ4OvZwJKdFk5BLk0MLbh51EOGUz9QGX2aCHfy1RvweSi7fgwA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4413,6 +4527,222 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@parcel/bundler-default@2.12.0': + resolution: {integrity: sha512-3ybN74oYNMKyjD6V20c9Gerdbh7teeNvVMwIoHIQMzuIFT6IGX53PyOLlOKRLbjxMc0TMimQQxIt2eQqxR5LsA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/cache@2.12.0': + resolution: {integrity: sha512-FX5ZpTEkxvq/yvWklRHDESVRz+c7sLTXgFuzz6uEnBcXV38j6dMSikflNpHA6q/L4GKkCqRywm9R6XQwhwIMyw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/codeframe@2.12.0': + resolution: {integrity: sha512-v2VmneILFiHZJTxPiR7GEF1wey1/IXPdZMcUlNXBiPZyWDfcuNgGGVQkx/xW561rULLIvDPharOMdxz5oHOKQg==} + engines: {node: '>= 12.0.0'} + + '@parcel/compressor-raw@2.12.0': + resolution: {integrity: sha512-h41Q3X7ZAQ9wbQ2csP8QGrwepasLZdXiuEdpUryDce6rF9ZiHoJ97MRpdLxOhOPyASTw/xDgE1xyaPQr0Q3f5A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/config-default@2.12.0': + resolution: {integrity: sha512-dPNe2n9eEsKRc1soWIY0yToMUPirPIa2QhxcCB3Z5RjpDGIXm0pds+BaiqY6uGLEEzsjhRO0ujd4v2Rmm0vuFg==} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/core@2.12.0': + resolution: {integrity: sha512-s+6pwEj+GfKf7vqGUzN9iSEPueUssCCQrCBUlcAfKrJe0a22hTUCjewpB0I7lNrCIULt8dkndD+sMdOrXsRl6Q==} + engines: {node: '>= 12.0.0'} + + '@parcel/diagnostic@2.12.0': + resolution: {integrity: sha512-8f1NOsSFK+F4AwFCKynyIu9Kr/uWHC+SywAv4oS6Bv3Acig0gtwUjugk0C9UaB8ztBZiW5TQZhw+uPZn9T/lJA==} + engines: {node: '>= 12.0.0'} + + '@parcel/events@2.12.0': + resolution: {integrity: sha512-nmAAEIKLjW1kB2cUbCYSmZOGbnGj8wCzhqnK727zCCWaA25ogzAtt657GPOeFyqW77KyosU728Tl63Fc8hphIA==} + engines: {node: '>= 12.0.0'} + + '@parcel/fs@2.12.0': + resolution: {integrity: sha512-NnFkuvou1YBtPOhTdZr44WN7I60cGyly2wpHzqRl62yhObyi1KvW0SjwOMa0QGNcBOIzp4G0CapoZ93hD0RG5Q==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/graph@3.2.0': + resolution: {integrity: sha512-xlrmCPqy58D4Fg5umV7bpwDx5Vyt7MlnQPxW68vae5+BA4GSWetfZt+Cs5dtotMG2oCHzZxhIPt7YZ7NRyQzLA==} + engines: {node: '>= 12.0.0'} + + '@parcel/logger@2.12.0': + resolution: {integrity: sha512-cJ7Paqa7/9VJ7C+KwgJlwMqTQBOjjn71FbKk0G07hydUEBISU2aDfmc/52o60ErL9l+vXB26zTrIBanbxS8rVg==} + engines: {node: '>= 12.0.0'} + + '@parcel/markdown-ansi@2.12.0': + resolution: {integrity: sha512-WZz3rzL8k0H3WR4qTHX6Ic8DlEs17keO9gtD4MNGyMNQbqQEvQ61lWJaIH0nAtgEetu0SOITiVqdZrb8zx/M7w==} + engines: {node: '>= 12.0.0'} + + '@parcel/namer-default@2.12.0': + resolution: {integrity: sha512-9DNKPDHWgMnMtqqZIMiEj/R9PNWW16lpnlHjwK3ciRlMPgjPJ8+UNc255teZODhX0T17GOzPdGbU/O/xbxVPzA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/node-resolver-core@3.3.0': + resolution: {integrity: sha512-rhPW9DYPEIqQBSlYzz3S0AjXxjN6Ub2yS6tzzsW/4S3Gpsgk/uEq4ZfxPvoPf/6TgZndVxmKwpmxaKtGMmf3cA==} + engines: {node: '>= 12.0.0'} + + '@parcel/optimizer-css@2.12.0': + resolution: {integrity: sha512-ifbcC97fRzpruTjaa8axIFeX4MjjSIlQfem3EJug3L2AVqQUXnM1XO8L0NaXGNLTW2qnh1ZjIJ7vXT/QhsphsA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-htmlnano@2.12.0': + resolution: {integrity: sha512-MfPMeCrT8FYiOrpFHVR+NcZQlXAptK2r4nGJjfT+ndPBhEEZp4yyL7n1y7HfX9geg5altc4WTb4Gug7rCoW8VQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-image@2.12.0': + resolution: {integrity: sha512-bo1O7raeAIbRU5nmNVtx8divLW9Xqn0c57GVNGeAK4mygnQoqHqRZ0mR9uboh64pxv6ijXZHPhKvU9HEpjPjBQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/optimizer-svgo@2.12.0': + resolution: {integrity: sha512-Kyli+ZZXnoonnbeRQdoWwee9Bk2jm/49xvnfb+2OO8NN0d41lblBoRhOyFiScRnJrw7eVl1Xrz7NTkXCIO7XFQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/optimizer-swc@2.12.0': + resolution: {integrity: sha512-iBi6LZB3lm6WmbXfzi8J3DCVPmn4FN2lw7DGXxUXu7MouDPVWfTsM6U/5TkSHJRNRogZ2gqy5q9g34NPxHbJcw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/package-manager@2.12.0': + resolution: {integrity: sha512-0nvAezcjPx9FT+hIL+LS1jb0aohwLZXct7jAh7i0MLMtehOi0z1Sau+QpgMlA9rfEZZ1LIeFdnZZwqSy7Ccspw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/packager-css@2.12.0': + resolution: {integrity: sha512-j3a/ODciaNKD19IYdWJT+TP+tnhhn5koBGBWWtrKSu0UxWpnezIGZetit3eE+Y9+NTePalMkvpIlit2eDhvfJA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-html@2.12.0': + resolution: {integrity: sha512-PpvGB9hFFe+19NXGz2ApvPrkA9GwEqaDAninT+3pJD57OVBaxB8U+HN4a5LICKxjUppPPqmrLb6YPbD65IX4RA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-js@2.12.0': + resolution: {integrity: sha512-viMF+FszITRRr8+2iJyk+4ruGiL27Y6AF7hQ3xbJfzqnmbOhGFtLTQwuwhOLqN/mWR2VKdgbLpZSarWaO3yAMg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-raw@2.12.0': + resolution: {integrity: sha512-tJZqFbHqP24aq1F+OojFbQIc09P/u8HAW5xfndCrFnXpW4wTgM3p03P0xfw3gnNq+TtxHJ8c3UFE5LnXNNKhYA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-svg@2.12.0': + resolution: {integrity: sha512-ldaGiacGb2lLqcXas97k8JiZRbAnNREmcvoY2W2dvW4loVuDT9B9fU777mbV6zODpcgcHWsLL3lYbJ5Lt3y9cg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/packager-wasm@2.12.0': + resolution: {integrity: sha512-fYqZzIqO9fGYveeImzF8ll6KRo2LrOXfD+2Y5U3BiX/wp9wv17dz50QLDQm9hmTcKGWxK4yWqKQh+Evp/fae7A==} + engines: {node: '>=12.0.0', parcel: ^2.12.0} + + '@parcel/plugin@2.12.0': + resolution: {integrity: sha512-nc/uRA8DiMoe4neBbzV6kDndh/58a4wQuGKw5oEoIwBCHUvE2W8ZFSu7ollSXUGRzfacTt4NdY8TwS73ScWZ+g==} + engines: {node: '>= 12.0.0'} + + '@parcel/profiler@2.12.0': + resolution: {integrity: sha512-q53fvl5LDcFYzMUtSusUBZSjQrKjMlLEBgKeQHFwkimwR1mgoseaDBDuNz0XvmzDzF1UelJ02TUKCGacU8W2qA==} + engines: {node: '>= 12.0.0'} + + '@parcel/reporter-cli@2.12.0': + resolution: {integrity: sha512-TqKsH4GVOLPSCanZ6tcTPj+rdVHERnt5y4bwTM82cajM21bCX1Ruwp8xOKU+03091oV2pv5ieB18pJyRF7IpIw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/reporter-dev-server@2.12.0': + resolution: {integrity: sha512-tIcDqRvAPAttRlTV28dHcbWT5K2r/MBFks7nM4nrEDHWtnrCwimkDmZTc1kD8QOCCjGVwRHcQybpHvxfwol6GA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/reporter-tracer@2.12.0': + resolution: {integrity: sha512-g8rlu9GxB8Ut/F8WGx4zidIPQ4pcYFjU9bZO+fyRIPrSUFH2bKijCnbZcr4ntqzDGx74hwD6cCG4DBoleq2UlQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/resolver-default@2.12.0': + resolution: {integrity: sha512-uuhbajTax37TwCxu7V98JtRLiT6hzE4VYSu5B7Qkauy14/WFt2dz6GOUXPgVsED569/hkxebPx3KCMtZW6cHHA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-browser-hmr@2.12.0': + resolution: {integrity: sha512-4ZLp2FWyD32r0GlTulO3+jxgsA3oO1P1b5oO2IWuWilfhcJH5LTiazpL5YdusUjtNn9PGN6QLAWfxmzRIfM+Ow==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-js@2.12.0': + resolution: {integrity: sha512-sBerP32Z1crX5PfLNGDSXSdqzlllM++GVnVQVeM7DgMKS8JIFG3VLi28YkX+dYYGtPypm01JoIHCkvwiZEcQJg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-react-refresh@2.12.0': + resolution: {integrity: sha512-SCHkcczJIDFTFdLTzrHTkQ0aTrX3xH6jrA4UsCBL6ji61+w+ohy4jEEe9qCgJVXhnJfGLE43HNXek+0MStX+Mw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/runtime-service-worker@2.12.0': + resolution: {integrity: sha512-BXuMBsfiwpIEnssn+jqfC3jkgbS8oxeo3C7xhSQsuSv+AF2FwY3O3AO1c1RBskEW3XrBLNINOJujroNw80VTKA==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/rust@2.12.0': + resolution: {integrity: sha512-005cldMdFZFDPOjbDVEXcINQ3wT4vrxvSavRWI3Az0e3E18exO/x/mW9f648KtXugOXMAqCEqhFHcXECL9nmMw==} + engines: {node: '>= 12.0.0'} + + '@parcel/source-map@2.1.1': + resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} + engines: {node: ^12.18.3 || >=14} + + '@parcel/transformer-babel@2.12.0': + resolution: {integrity: sha512-zQaBfOnf/l8rPxYGnsk/ufh/0EuqvmnxafjBIpKZ//j6rGylw5JCqXSb1QvvAqRYruKeccxGv7+HrxpqKU6V4A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-css@2.12.0': + resolution: {integrity: sha512-vXhOqoAlQGATYyQ433Z1DXKmiKmzOAUmKysbYH3FD+LKEKLMEl/pA14goqp00TW+A/EjtSKKyeMyHlMIIUqj4Q==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-html@2.12.0': + resolution: {integrity: sha512-5jW4dFFBlYBvIQk4nrH62rfA/G/KzVzEDa6S+Nne0xXhglLjkm64Ci9b/d4tKZfuGWUbpm2ASAq8skti/nfpXw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-image@2.12.0': + resolution: {integrity: sha512-8hXrGm2IRII49R7lZ0RpmNk27EhcsH+uNKsvxuMpXPuEnWgC/ha/IrjaI29xCng1uGur74bJF43NUSQhR4aTdw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/transformer-js@2.12.0': + resolution: {integrity: sha512-OSZpOu+FGDbC/xivu24v092D9w6EGytB3vidwbdiJ2FaPgfV7rxS0WIUjH4I0OcvHAcitArRXL0a3+HrNTdQQw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + peerDependencies: + '@parcel/core': ^2.12.0 + + '@parcel/transformer-json@2.12.0': + resolution: {integrity: sha512-Utv64GLRCQILK5r0KFs4o7I41ixMPllwOLOhkdjJKvf1hZmN6WqfOmB1YLbWS/y5Zb/iB52DU2pWZm96vLFQZQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-postcss@2.12.0': + resolution: {integrity: sha512-FZqn+oUtiLfPOn67EZxPpBkfdFiTnF4iwiXPqvst3XI8H+iC+yNgzmtJkunOOuylpYY6NOU5jT8d7saqWSDv2Q==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-posthtml@2.12.0': + resolution: {integrity: sha512-z6Z7rav/pcaWdeD+2sDUcd0mmNZRUvtHaUGa50Y2mr+poxrKilpsnFMSiWBT+oOqPt7j71jzDvrdnAF4XkCljg==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-raw@2.12.0': + resolution: {integrity: sha512-Ht1fQvXxix0NncdnmnXZsa6hra20RXYh1VqhBYZLsDfkvGGFnXIgO03Jqn4Z8MkKoa0tiNbDhpKIeTjyclbBxQ==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-react-refresh-wrap@2.12.0': + resolution: {integrity: sha512-GE8gmP2AZtkpBIV5vSCVhewgOFRhqwdM5Q9jNPOY5PKcM3/Ff0qCqDiTzzGLhk0/VMBrdjssrfZkVx6S/lHdJw==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/transformer-svg@2.12.0': + resolution: {integrity: sha512-cZJqGRJ4JNdYcb+vj94J7PdOuTnwyy45dM9xqbIMH+HSiiIkfrMsdEwYft0GTyFTdsnf+hdHn3tau7Qa5hhX+A==} + engines: {node: '>= 12.0.0', parcel: ^2.12.0} + + '@parcel/types@2.12.0': + resolution: {integrity: sha512-8zAFiYNCwNTQcglIObyNwKfRYQK5ELlL13GuBOrSMxueUiI5ylgsGbTS1N7J3dAGZixHO8KhHGv5a71FILn9rQ==} + + '@parcel/utils@2.12.0': + resolution: {integrity: sha512-z1JhLuZ8QmDaYoEIuUCVZlhcFrS7LMfHrb2OCRui5SQFntRWBH2fNM6H/fXXUkT9SkxcuFP2DUA6/m4+Gkz72g==} + engines: {node: '>= 12.0.0'} + '@parcel/watcher-android-arm64@2.4.1': resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} engines: {node: '>= 10.0.0'} @@ -4489,6 +4819,12 @@ packages: resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==} engines: {node: '>= 10.0.0'} + '@parcel/workers@2.12.0': + resolution: {integrity: sha512-zv5We5Jmb+ZWXlU6A+AufyjY4oZckkxsZ8J4dvyWL0W8IQvGO1JB4FGeryyttzQv3RM3OxcN/BpTGPiDG6keBw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.12.0 + '@peculiar/asn1-schema@2.3.8': resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} @@ -6231,6 +6567,9 @@ packages: '@tanstack/query-core@5.50.1': resolution: {integrity: sha512-lpfhKPrJlyV2DSVcQb/HuozH3Av3kws4ge22agx+lNGpFkS4vLZ7St0l3GLwlAD+bqB+qXGex3JdRKUNtMviEQ==} + '@tanstack/query-core@5.59.0': + resolution: {integrity: sha512-WGD8uIhX6/deH/tkZqPNcRyAhDUqs729bWKoByYHSogcshXfFbppOdTER5+qY7mFvu8KEFJwT0nxr8RfPTVh0Q==} + '@tanstack/query-devtools@5.51.1': resolution: {integrity: sha512-rehG0WmL3EXER6MAI2uHQia/n0b5c3ZROohpYm7u3G7yg4q+HsfQy6nuAo6uy40NzHUe3FmnfWCZQ0Vb/3lE6g==} @@ -6253,6 +6592,11 @@ packages: peerDependencies: react: ^18.0.0 + '@tanstack/react-query@5.59.0': + resolution: {integrity: sha512-YDXp3OORbYR+8HNQx+lf4F73NoiCmCcSvZvgxE29OifmQFk0sBlO26NWLHpcNERo92tVk3w+JQ53/vkcRUY1hA==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-virtual@3.5.0': resolution: {integrity: sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==} peerDependencies: @@ -7086,6 +7430,9 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + abortcontroller-polyfill@1.7.5: + resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -7526,6 +7873,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-x@3.0.10: + resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + base-x@5.0.0: resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} @@ -7903,6 +8253,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -8116,6 +8470,15 @@ packages: typescript: optional: true + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -8182,6 +8545,10 @@ packages: css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -8205,6 +8572,10 @@ packages: engines: {node: '>=4'} hasBin: true + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -8591,6 +8962,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} @@ -8704,6 +9079,9 @@ packages: resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} engines: {node: '>=12'} + dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dotenv-webpack@8.1.0: resolution: {integrity: sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag==} engines: {node: '>=10'} @@ -8714,6 +9092,10 @@ packages: resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==} engines: {node: '>=12'} + dotenv@7.0.0: + resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} + engines: {node: '>=6'} + dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} @@ -8787,6 +9169,10 @@ packages: entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -9117,11 +9503,13 @@ packages: eslint@8.36.0: resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true eslint@8.45.0: resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true eslint@9.6.0: @@ -9592,6 +9980,10 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + get-port@4.2.0: + resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} + engines: {node: '>=6'} + get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} @@ -9973,9 +10365,41 @@ packages: webpack: optional: true + htmlnano@2.1.1: + resolution: {integrity: sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==} + peerDependencies: + cssnano: ^7.0.0 + postcss: '>=8.4.31' + purgecss: ^6.0.0 + relateurl: ^0.2.7 + srcset: 5.0.1 + svgo: ^3.0.2 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -10299,6 +10723,9 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-json@2.0.1: + resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} + is-lower-case@2.0.2: resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} @@ -10737,6 +11164,70 @@ packages: lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + lightningcss-darwin-arm64@1.27.0: + resolution: {integrity: sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.27.0: + resolution: {integrity: sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.27.0: + resolution: {integrity: sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.27.0: + resolution: {integrity: sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.27.0: + resolution: {integrity: sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.27.0: + resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.27.0: + resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.27.0: + resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.27.0: + resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.27.0: + resolution: {integrity: sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.27.0: + resolution: {integrity: sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==} + engines: {node: '>= 12.0.0'} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -10764,6 +11255,10 @@ packages: enquirer: optional: true + lmdb@2.8.5: + resolution: {integrity: sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==} + hasBin: true + load-yaml-file@0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} @@ -10991,6 +11486,9 @@ packages: mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -11305,6 +11803,13 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.0: + resolution: {integrity: sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==} + msw@2.3.1: resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==} engines: {node: '>=18'} @@ -11437,6 +11942,9 @@ packages: node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + node-addon-api@7.1.0: resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} engines: {node: ^16 || ^18 || >= 20} @@ -11478,6 +11986,14 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} + hasBin: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -11656,6 +12172,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ordered-binary@1.5.2: + resolution: {integrity: sha512-JTo+4+4Fw7FreyAvlSLjb1BBVaxEQAacmjD3jjuyPZclpbEghTvQZbXBb2qPd2LeIMxiHwXBZUcpmG2Gl/mDEA==} + os-locale@5.0.0: resolution: {integrity: sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==} engines: {node: '>=10'} @@ -11754,6 +12273,11 @@ packages: param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parcel@2.12.0: + resolution: {integrity: sha512-W+gxAq7aQ9dJIg/XLKGcRT0cvnStFAQHPaI0pvD0U2l6IVLueUAm3nwN7lkY62zZNmlvNx6jNtE4wlbS+CyqSg==} + engines: {node: '>= 12.0.0'} + hasBin: true + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -12243,6 +12767,22 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + posthtml-parser@0.10.2: + resolution: {integrity: sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==} + engines: {node: '>=12'} + + posthtml-parser@0.11.0: + resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} + engines: {node: '>=12'} + + posthtml-render@3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + + posthtml@0.16.6: + resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} + engines: {node: '>=12.0.0'} + preferred-pm@3.1.4: resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} engines: {node: '>=10'} @@ -12517,6 +13057,9 @@ packages: peerDependencies: react: '>=16.13.1' + react-error-overlay@6.0.9: + resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + react-fast-compare@2.0.4: resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} @@ -12579,6 +13122,10 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-refresh@0.9.0: + resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.4: resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} @@ -13261,11 +13808,19 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + srcset@4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + sshpk@1.17.0: resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==} engines: {node: '>=0.10.0'} hasBin: true + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -13492,6 +14047,11 @@ packages: svg-parser@2.0.4: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + svgo@3.0.2: resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} engines: {node: '>=14.0.0'} @@ -13621,6 +14181,9 @@ packages: throwback@4.1.0: resolution: {integrity: sha512-dLFe8bU8SeH0xeqeKL7BNo8XoPC/o91nz9/ooeplZPiso+DZukhoyZcSz9TFnUNScm+cA9qjU1m1853M6sPOng==} + timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} @@ -14224,6 +14787,10 @@ packages: utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -14402,6 +14969,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + web-ext@7.6.2: resolution: {integrity: sha512-xlxbzgFBIS/UWWlvWxyR1PIqRRzDj1cutoHh+VZu4ZTcJTfv35KVdKkLRZv4PQwHu4dg8VfTg7WEcNP4QLaaFQ==} engines: {node: '>=14.0.0', npm: '>=6.9.0'} @@ -14880,7 +15450,7 @@ snapshots: '@amplitude/utils@1.10.2': dependencies: '@amplitude/types': 1.10.2 - tslib: 2.6.3 + tslib: 2.7.0 '@ampproject/remapping@2.3.0': dependencies: @@ -17137,7 +17707,7 @@ snapshots: '@graphql-tools/utils': 10.0.13(graphql@16.9.0) dataloader: 2.2.2 graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 value-or-promise: 1.0.12 '@graphql-tools/code-file-loader@8.1.0(graphql@16.9.0)': @@ -17159,13 +17729,13 @@ snapshots: '@graphql-tools/utils': 10.0.13(graphql@16.9.0) dataloader: 2.2.2 graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 '@graphql-tools/documents@1.0.0(graphql@16.9.0)': dependencies: graphql: 16.9.0 lodash.sortby: 4.7.0 - tslib: 2.6.3 + tslib: 2.7.0 '@graphql-tools/executor-graphql-ws@1.1.0(graphql@16.9.0)': dependencies: @@ -17174,7 +17744,7 @@ snapshots: graphql: 16.9.0 graphql-ws: 5.14.3(graphql@16.9.0) isomorphic-ws: 5.0.0(ws@8.18.0) - tslib: 2.6.3 + tslib: 2.7.0 ws: 8.18.0 transitivePeerDependencies: - bufferutil @@ -17188,7 +17758,7 @@ snapshots: extract-files: 11.0.0 graphql: 16.9.0 meros: 1.3.0(@types/node@20.14.10) - tslib: 2.6.3 + tslib: 2.7.0 value-or-promise: 1.0.12 transitivePeerDependencies: - '@types/node' @@ -17199,7 +17769,7 @@ snapshots: '@types/ws': 8.5.10 graphql: 16.9.0 isomorphic-ws: 5.0.0(ws@8.18.0) - tslib: 2.6.3 + tslib: 2.7.0 ws: 8.18.0 transitivePeerDependencies: - bufferutil @@ -17211,7 +17781,7 @@ snapshots: '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) '@repeaterjs/repeater': 3.0.5 graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 value-or-promise: 1.0.12 '@graphql-tools/git-loader@8.0.4(graphql@16.9.0)': @@ -17259,7 +17829,7 @@ snapshots: '@babel/types': 7.23.9 '@graphql-tools/utils': 10.0.13(graphql@16.9.0) graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color @@ -17268,7 +17838,7 @@ snapshots: '@graphql-tools/utils': 10.0.13(graphql@16.9.0) graphql: 16.9.0 resolve-from: 5.0.0 - tslib: 2.6.3 + tslib: 2.7.0 '@graphql-tools/json-file-loader@8.0.0(graphql@16.9.0)': dependencies: @@ -17295,7 +17865,7 @@ snapshots: '@graphql-tools/optimize@2.0.0(graphql@16.9.0)': dependencies: graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 '@graphql-tools/prisma-loader@8.0.2(@types/node@20.14.10)(graphql@16.9.0)': dependencies: @@ -17330,7 +17900,7 @@ snapshots: '@ardatan/relay-compiler': 12.0.0(graphql@16.9.0) '@graphql-tools/utils': 10.0.13(graphql@16.9.0) graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - encoding - supports-color @@ -17340,7 +17910,7 @@ snapshots: '@ardatan/relay-compiler': 12.0.0(graphql@16.9.0) '@graphql-tools/utils': 10.3.1(graphql@16.9.0) graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - encoding - supports-color @@ -17350,7 +17920,7 @@ snapshots: '@graphql-tools/merge': 9.0.1(graphql@16.9.0) '@graphql-tools/utils': 10.0.13(graphql@16.9.0) graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 value-or-promise: 1.0.12 '@graphql-tools/url-loader@8.0.1(@types/node@20.14.10)(graphql@16.9.0)': @@ -17389,7 +17959,7 @@ snapshots: cross-inspect: 1.0.0 dset: 3.1.3 graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 '@graphql-tools/wrap@10.0.1(graphql@16.9.0)': dependencies: @@ -17397,7 +17967,7 @@ snapshots: '@graphql-tools/schema': 10.0.2(graphql@16.9.0) '@graphql-tools/utils': 10.0.13(graphql@16.9.0) graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 value-or-promise: 1.0.12 '@graphql-typed-document-node/core@3.2.0(graphql@16.9.0)': @@ -17639,6 +18209,30 @@ snapshots: '@ledgerhq/logs@6.12.0': {} + '@lezer/common@1.2.2': {} + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.2 + + '@lmdb/lmdb-darwin-arm64@2.8.5': + optional: true + + '@lmdb/lmdb-darwin-x64@2.8.5': + optional: true + + '@lmdb/lmdb-linux-arm64@2.8.5': + optional: true + + '@lmdb/lmdb-linux-arm@2.8.5': + optional: true + + '@lmdb/lmdb-linux-x64@2.8.5': + optional: true + + '@lmdb/lmdb-win32-x64@2.8.5': + optional: true + '@manypkg/cli@0.21.4': dependencies: '@manypkg/get-packages': 2.2.2 @@ -17730,6 +18324,30 @@ snapshots: '@microsoft/tsdoc@0.14.2': {} + '@mischnic/json-sourcemap@0.1.1': + dependencies: + '@lezer/common': 1.2.2 + '@lezer/lr': 1.4.2 + json5: 2.2.3 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + '@mswjs/cookies@1.1.1': {} '@mswjs/interceptors@0.29.1': @@ -17741,6 +18359,71 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@mysten/bcs@1.1.0': + dependencies: + bs58: 6.0.0 + + '@mysten/dapp-kit@0.14.25(@tanstack/react-query@5.59.0(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.3)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)': + dependencies: + '@mysten/sui': 1.12.0(typescript@5.5.3) + '@mysten/wallet-standard': 0.13.7(typescript@5.5.3) + '@mysten/zksend': 0.11.6(typescript@5.5.3) + '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@tanstack/react-query': 5.59.0(react@18.3.1) + '@vanilla-extract/css': 1.15.3(babel-plugin-macros@3.1.0) + '@vanilla-extract/dynamic': 2.1.1 + '@vanilla-extract/recipes': 0.5.3(@vanilla-extract/css@1.15.3(babel-plugin-macros@3.1.0)) + clsx: 2.1.1 + react: 18.3.1 + zustand: 4.5.4(@types/react@18.3.3)(immer@9.0.21)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - babel-plugin-macros + - immer + - react-dom + - svelte + - typescript + + '@mysten/sui@1.12.0(typescript@5.5.3)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@mysten/bcs': 1.1.0 + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + '@suchipi/femver': 1.0.0 + bech32: 2.0.0 + gql.tada: 1.8.2(graphql@16.9.0)(typescript@5.5.3) + graphql: 16.9.0 + tweetnacl: 1.0.3 + valibot: 0.36.0 + transitivePeerDependencies: + - svelte + - typescript + + '@mysten/wallet-standard@0.13.7(typescript@5.5.3)': + dependencies: + '@mysten/sui': 1.12.0(typescript@5.5.3) + '@wallet-standard/core': 1.0.3 + transitivePeerDependencies: + - svelte + - typescript + + '@mysten/zksend@0.11.6(typescript@5.5.3)': + dependencies: + '@mysten/sui': 1.12.0(typescript@5.5.3) + '@mysten/wallet-standard': 0.13.7(typescript@5.5.3) + mitt: 3.0.1 + nanostores: 0.10.3 + valibot: 0.36.0 + transitivePeerDependencies: + - svelte + - typescript + '@nanostores/react@0.7.2(nanostores@0.10.3)(react@18.3.1)': dependencies: nanostores: 0.10.3 @@ -17872,7 +18555,7 @@ snapshots: debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-wsl: 2.2.0 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color @@ -17894,7 +18577,7 @@ snapshots: debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-wsl: 2.2.0 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color @@ -17965,50 +18648,564 @@ snapshots: transitivePeerDependencies: - supports-color - '@oclif/plugin-update@1.5.0': + '@oclif/plugin-update@1.5.0': + dependencies: + '@oclif/color': 0.1.2 + '@oclif/command': 1.8.36(supports-color@8.1.1) + '@oclif/config': 1.18.17(supports-color@8.1.1) + '@oclif/errors': 1.3.6 + '@types/semver': 7.5.8 + cli-ux: 5.6.7 + cross-spawn: 7.0.3 + debug: 4.3.5(supports-color@8.1.1) + filesize: 6.4.0 + fs-extra: 9.1.0 + http-call: 5.3.0 + lodash: 4.17.21 + log-chopper: 1.0.2 + semver: 7.6.2 + tar-fs: 2.1.1 + transitivePeerDependencies: + - supports-color + + '@oclif/plugin-warn-if-update-available@1.7.3': + dependencies: + '@oclif/command': 1.8.36(supports-color@8.1.1) + '@oclif/config': 1.18.17(supports-color@8.1.1) + '@oclif/errors': 1.3.6 + chalk: 4.1.2 + debug: 4.3.5(supports-color@8.1.1) + fs-extra: 9.1.0 + http-call: 5.3.0 + lodash: 4.17.21 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + '@oclif/screen@1.0.4': {} + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@parcel/bundler-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/graph': 3.2.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/logger': 2.12.0 + '@parcel/utils': 2.12.0 + lmdb: 2.8.5 + + '@parcel/codeframe@2.12.0': + dependencies: + chalk: 4.1.2 + + '@parcel/compressor-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/config-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3)': + dependencies: + '@parcel/bundler-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/compressor-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/namer-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/optimizer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/optimizer-htmlnano': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3) + '@parcel/optimizer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/optimizer-svgo': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/optimizer-swc': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/packager-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/packager-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/packager-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/packager-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/packager-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/packager-wasm': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/resolver-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/runtime-browser-hmr': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/runtime-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/runtime-react-refresh': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/runtime-service-worker': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-babel': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-json': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-postcss': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-posthtml': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-react-refresh-wrap': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/transformer-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/core@2.12.0(@swc/helpers@0.5.5)': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/graph': 3.2.0 + '@parcel/logger': 2.12.0 + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/profiler': 2.12.0 + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + abortcontroller-polyfill: 1.7.5 + base-x: 3.0.10 + browserslist: 4.23.1 + clone: 2.1.2 + dotenv: 7.0.0 + dotenv-expand: 5.1.0 + json5: 2.2.3 + msgpackr: 1.11.0 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/diagnostic@2.12.0': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + nullthrows: 1.1.1 + + '@parcel/events@2.12.0': {} + + '@parcel/fs@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/rust': 2.12.0 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + '@parcel/watcher': 2.4.1 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/graph@3.2.0': + dependencies: + nullthrows: 1.1.1 + + '@parcel/logger@2.12.0': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + + '@parcel/markdown-ansi@2.12.0': + dependencies: + chalk: 4.1.2 + + '@parcel/namer-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/node-resolver-core@3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.1 + lightningcss: 1.27.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-htmlnano@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3)': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + htmlnano: 2.1.1(postcss@8.4.39)(relateurl@0.2.7)(svgo@2.8.0)(terser@5.31.1)(typescript@5.5.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/optimizer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + + '@parcel/optimizer-svgo@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-swc@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + '@swc/core': 1.6.13(@swc/helpers@0.5.5) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' + + '@parcel/package-manager@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/logger': 2.12.0 + '@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@swc/core': 1.6.13(@swc/helpers@0.5.5) + semver: 7.6.2 + transitivePeerDependencies: + - '@swc/helpers' + + '@parcel/packager-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + lightningcss: 1.27.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + globals: 13.24.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-wasm@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/plugin@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/profiler@2.12.0': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + chrome-trace-event: 1.0.4 + + '@parcel/reporter-cli@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + chalk: 4.1.2 + term-size: 2.2.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/reporter-dev-server@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/reporter-tracer@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + chrome-trace-event: 1.0.4 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/resolver-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-browser-hmr@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-react-refresh@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + react-error-overlay: 6.0.9 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-service-worker@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/rust@2.12.0': {} + + '@parcel/source-map@2.1.1': + dependencies: + detect-libc: 1.0.3 + + '@parcel/transformer-babel@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.1 + json5: 2.2.3 + nullthrows: 1.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + browserslist: 4.23.1 + lightningcss: 1.27.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.2 + srcset: 4.0.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + nullthrows: 1.1.1 + + '@parcel/transformer-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.12.0 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@swc/helpers': 0.5.5 + browserslist: 4.23.1 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.11 + semver: 7.6.2 + + '@parcel/transformer-json@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + json5: 2.2.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-postcss@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + '@parcel/utils': 2.12.0 + clone: 2.1.2 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-posthtml@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': dependencies: - '@oclif/color': 0.1.2 - '@oclif/command': 1.8.36(supports-color@8.1.1) - '@oclif/config': 1.18.17(supports-color@8.1.1) - '@oclif/errors': 1.3.6 - '@types/semver': 7.5.8 - cli-ux: 5.6.7 - cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@8.1.1) - filesize: 6.4.0 - fs-extra: 9.1.0 - http-call: 5.3.0 - lodash: 4.17.21 - log-chopper: 1.0.2 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 semver: 7.6.2 - tar-fs: 2.1.1 transitivePeerDependencies: - - supports-color + - '@parcel/core' - '@oclif/plugin-warn-if-update-available@1.7.3': + '@parcel/transformer-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': dependencies: - '@oclif/command': 1.8.36(supports-color@8.1.1) - '@oclif/config': 1.18.17(supports-color@8.1.1) - '@oclif/errors': 1.3.6 - chalk: 4.1.2 - debug: 4.3.5(supports-color@8.1.1) - fs-extra: 9.1.0 - http-call: 5.3.0 - lodash: 4.17.21 - semver: 7.6.2 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) transitivePeerDependencies: - - supports-color + - '@parcel/core' - '@oclif/screen@1.0.4': {} + '@parcel/transformer-react-refresh-wrap@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' - '@open-draft/deferred-promise@2.2.0': {} + '@parcel/transformer-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/diagnostic': 2.12.0 + '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/rust': 2.12.0 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.2 + transitivePeerDependencies: + - '@parcel/core' - '@open-draft/logger@0.3.0': + '@parcel/types@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)': dependencies: - is-node-process: 1.2.0 - outvariant: 1.4.3 + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/diagnostic': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/source-map': 2.1.1 + '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + utility-types: 3.11.0 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' - '@open-draft/until@2.1.0': {} + '@parcel/utils@2.12.0': + dependencies: + '@parcel/codeframe': 2.12.0 + '@parcel/diagnostic': 2.12.0 + '@parcel/logger': 2.12.0 + '@parcel/markdown-ansi': 2.12.0 + '@parcel/rust': 2.12.0 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + nullthrows: 1.1.1 '@parcel/watcher-android-arm64@2.4.1': optional: true @@ -18066,22 +19263,32 @@ snapshots: '@parcel/watcher-win32-ia32': 2.4.1 '@parcel/watcher-win32-x64': 2.4.1 + '@parcel/workers@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))': + dependencies: + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/diagnostic': 2.12.0 + '@parcel/logger': 2.12.0 + '@parcel/profiler': 2.12.0 + '@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/utils': 2.12.0 + nullthrows: 1.1.1 + '@peculiar/asn1-schema@2.3.8': dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.6.3 + tslib: 2.7.0 '@peculiar/json-schema@1.1.12': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 '@peculiar/webcrypto@1.4.5': dependencies: '@peculiar/asn1-schema': 2.3.8 '@peculiar/json-schema': 1.1.12 pvtsutils: 1.3.5 - tslib: 2.6.3 + tslib: 2.7.0 webcrypto-core: 1.7.8 '@phenomnomnominal/tsquery@3.0.0(typescript@3.9.10)': @@ -19247,7 +20454,7 @@ snapshots: '@sentry/core': 7.59.2 '@sentry/types': 7.59.2 '@sentry/utils': 7.59.2 - tslib: 2.6.2 + tslib: 2.7.0 '@sentry-internal/tracing@7.61.0': dependencies: @@ -19299,7 +20506,7 @@ snapshots: dependencies: '@sentry/types': 7.59.2 '@sentry/utils': 7.59.2 - tslib: 2.6.2 + tslib: 2.7.0 '@sentry/core@7.61.0': dependencies: @@ -20127,7 +21334,7 @@ snapshots: flat-cache: 3.2.0 micromatch: 4.0.7 react-docgen-typescript: 2.2.2(typescript@5.5.3) - tslib: 2.6.3 + tslib: 2.7.0 typescript: 5.5.3 webpack: 5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1)) transitivePeerDependencies: @@ -20458,6 +21665,8 @@ snapshots: '@tanstack/query-core@5.50.1': {} + '@tanstack/query-core@5.59.0': {} + '@tanstack/query-devtools@5.51.1': {} '@tanstack/query-persist-client-core@4.29.25': @@ -20480,6 +21689,11 @@ snapshots: '@tanstack/query-core': 5.50.1 react: 18.3.1 + '@tanstack/react-query@5.59.0(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.59.0 + react: 18.3.1 + '@tanstack/react-virtual@3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/virtual-core': 3.5.0 @@ -20499,7 +21713,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.6(vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1))': + '@testing-library/jest-dom@6.4.6(vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1))': dependencies: '@adobe/css-tools': 4.4.0 '@babel/runtime': 7.24.7 @@ -20510,7 +21724,7 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 optionalDependencies: - vitest: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1) + vitest: 2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) '@testing-library/react@16.0.0(@testing-library/dom@10.3.1)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -21236,9 +22450,9 @@ snapshots: dependencies: '@vanilla-extract/private': 1.0.5 - '@vanilla-extract/esbuild-plugin@2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(sass@1.77.6)(terser@5.31.1)': + '@vanilla-extract/esbuild-plugin@2.3.8(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(esbuild@0.23.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)': dependencies: - '@vanilla-extract/integration': 7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1) + '@vanilla-extract/integration': 7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) optionalDependencies: esbuild: 0.23.0 transitivePeerDependencies: @@ -21252,7 +22466,7 @@ snapshots: - supports-color - terser - '@vanilla-extract/integration@7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1)': + '@vanilla-extract/integration@7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)': dependencies: '@babel/core': 7.24.7 '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) @@ -21264,8 +22478,8 @@ snapshots: find-up: 5.0.0 javascript-stringify: 2.1.0 mlly: 1.7.1 - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) - vite-node: 1.6.0(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) + vite-node: 1.6.0(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -21283,10 +22497,10 @@ snapshots: dependencies: '@vanilla-extract/css': 1.15.3(babel-plugin-macros@3.1.0) - '@vanilla-extract/vite-plugin@4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))': + '@vanilla-extract/vite-plugin@4.0.13(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1))': dependencies: - '@vanilla-extract/integration': 7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(sass@1.77.6)(terser@5.31.1) - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + '@vanilla-extract/integration': 7.1.7(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -21298,21 +22512,21 @@ snapshots: - supports-color - terser - '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))': + '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.5)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1))': dependencies: '@swc/core': 1.6.13(@swc/helpers@0.5.5) - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))': + '@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1))': dependencies: '@babel/core': 7.24.7 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - supports-color @@ -21356,7 +22570,7 @@ snapshots: '@vue/shared': 3.4.31 entities: 4.5.0 estree-walker: 2.0.2 - source-map-js: 1.2.0 + source-map-js: 1.2.1 '@vue/compiler-dom@3.4.31': dependencies: @@ -21513,7 +22727,7 @@ snapshots: busboy: 1.6.0 fast-querystring: 1.1.2 fast-url-parser: 1.1.3 - tslib: 2.6.3 + tslib: 2.7.0 '@whatwg-node/node-fetch@0.5.5': dependencies: @@ -21521,7 +22735,7 @@ snapshots: '@whatwg-node/events': 0.1.1 busboy: 1.6.0 fast-querystring: 1.1.2 - tslib: 2.6.3 + tslib: 2.7.0 '@xtuc/ieee754@1.2.0': {} @@ -21530,7 +22744,7 @@ snapshots: '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.20)': dependencies: esbuild: 0.18.20 - tslib: 2.6.3 + tslib: 2.7.0 '@yarnpkg/fslib@2.10.3': dependencies: @@ -21546,6 +22760,8 @@ snapshots: dependencies: event-target-shim: 5.0.1 + abortcontroller-polyfill@1.7.5: {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -21871,7 +23087,7 @@ snapshots: dependencies: pvtsutils: 1.3.5 pvutils: 1.1.3 - tslib: 2.6.3 + tslib: 2.7.0 assert-plus@1.0.0: {} @@ -21889,11 +23105,11 @@ snapshots: ast-types@0.15.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 ast-types@0.16.1: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 astral-regex@2.0.0: {} @@ -22089,6 +23305,10 @@ snapshots: balanced-match@1.0.2: {} + base-x@3.0.10: + dependencies: + safe-buffer: 5.2.1 + base-x@5.0.0: {} base64-js@1.5.1: {} @@ -22274,7 +23494,7 @@ snapshots: camel-case@4.1.2: dependencies: pascal-case: 3.1.2 - tslib: 2.6.3 + tslib: 2.7.0 camelcase-css@2.0.1: {} @@ -22291,7 +23511,7 @@ snapshots: capital-case@1.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 upper-case-first: 2.0.2 cardinal@2.1.1: @@ -22363,7 +23583,7 @@ snapshots: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 character-entities-html4@2.1.0: {} @@ -22509,7 +23729,7 @@ snapshots: strip-ansi: 6.0.1 supports-color: 8.1.1 supports-hyperlinks: 2.3.0 - tslib: 2.6.3 + tslib: 2.7.0 cli-width@3.0.0: {} @@ -22547,6 +23767,8 @@ snapshots: clone@1.0.4: {} + clone@2.1.2: {} + clsx@2.0.0: {} clsx@2.1.1: {} @@ -22690,7 +23912,7 @@ snapshots: constant-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 upper-case: 2.0.2 constants-browserify@1.0.0: {} @@ -22766,6 +23988,15 @@ snapshots: optionalDependencies: typescript: 5.5.3 + cosmiconfig@9.0.0(typescript@5.5.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.5.3 + create-require@1.1.1: {} cross-env@7.0.3: @@ -22780,7 +24011,7 @@ snapshots: cross-inspect@1.0.0: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 cross-spawn@5.1.0: dependencies: @@ -22845,15 +24076,20 @@ snapshots: domutils: 3.0.1 nth-check: 2.0.1 + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-what@6.1.0: {} @@ -22863,6 +24099,10 @@ snapshots: cssesc@3.0.0: {} + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -23231,6 +24471,8 @@ snapshots: detect-libc@1.0.3: {} + detect-libc@2.0.3: {} + detect-node-es@1.1.0: {} detect-package-manager@2.0.1: @@ -23331,7 +24573,7 @@ snapshots: dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 dot-prop@5.3.0: dependencies: @@ -23347,6 +24589,8 @@ snapshots: dotenv-expand@10.0.0: {} + dotenv-expand@5.1.0: {} + dotenv-webpack@8.1.0(webpack@5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1))): dependencies: dotenv-defaults: 2.0.2 @@ -23354,6 +24598,8 @@ snapshots: dotenv@16.4.1: {} + dotenv@7.0.0: {} + dotenv@8.6.0: {} dset@3.1.3: {} @@ -23423,6 +24669,8 @@ snapshots: entities@2.2.0: {} + entities@3.0.1: {} + entities@4.5.0: {} env-paths@2.2.1: {} @@ -24666,6 +25914,8 @@ snapshots: get-package-type@0.1.0: {} + get-port@4.2.0: {} + get-port@5.1.1: {} get-stream@3.0.0: {} @@ -25144,7 +26394,7 @@ snapshots: header-case@2.0.4: dependencies: capital-case: 1.0.4 - tslib: 2.6.3 + tslib: 2.7.0 headers-polyfill@4.0.3: {} @@ -25186,6 +26436,19 @@ snapshots: optionalDependencies: webpack: 5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1)) + htmlnano@2.1.1(postcss@8.4.39)(relateurl@0.2.7)(svgo@2.8.0)(terser@5.31.1)(typescript@5.5.3): + dependencies: + cosmiconfig: 9.0.0(typescript@5.5.3) + posthtml: 0.16.6 + timsort: 0.3.0 + optionalDependencies: + postcss: 8.4.39 + relateurl: 0.2.7 + svgo: 2.8.0 + terser: 5.31.1 + transitivePeerDependencies: + - typescript + htmlparser2@6.1.0: dependencies: domelementtype: 2.3.0 @@ -25193,6 +26456,13 @@ snapshots: domutils: 2.8.0 entities: 2.2.0 + htmlparser2@7.2.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -25514,9 +26784,11 @@ snapshots: is-interactive@1.0.0: {} + is-json@2.0.1: {} + is-lower-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 is-mergeable-object@1.1.1: {} @@ -25628,7 +26900,7 @@ snapshots: is-upper-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 is-utf8@0.2.1: {} @@ -25977,6 +27249,51 @@ snapshots: transitivePeerDependencies: - supports-color + lightningcss-darwin-arm64@1.27.0: + optional: true + + lightningcss-darwin-x64@1.27.0: + optional: true + + lightningcss-freebsd-x64@1.27.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.27.0: + optional: true + + lightningcss-linux-arm64-gnu@1.27.0: + optional: true + + lightningcss-linux-arm64-musl@1.27.0: + optional: true + + lightningcss-linux-x64-gnu@1.27.0: + optional: true + + lightningcss-linux-x64-musl@1.27.0: + optional: true + + lightningcss-win32-arm64-msvc@1.27.0: + optional: true + + lightningcss-win32-x64-msvc@1.27.0: + optional: true + + lightningcss@1.27.0: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.27.0 + lightningcss-darwin-x64: 1.27.0 + lightningcss-freebsd-x64: 1.27.0 + lightningcss-linux-arm-gnueabihf: 1.27.0 + lightningcss-linux-arm64-gnu: 1.27.0 + lightningcss-linux-arm64-musl: 1.27.0 + lightningcss-linux-x64-gnu: 1.27.0 + lightningcss-linux-x64-musl: 1.27.0 + lightningcss-win32-arm64-msvc: 1.27.0 + lightningcss-win32-x64-msvc: 1.27.0 + lilconfig@2.1.0: {} lilconfig@3.1.2: {} @@ -26002,6 +27319,21 @@ snapshots: optionalDependencies: enquirer: 2.4.1 + lmdb@2.8.5: + dependencies: + msgpackr: 1.11.0 + node-addon-api: 6.1.0 + node-gyp-build-optional-packages: 5.1.1 + ordered-binary: 1.5.2 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 2.8.5 + '@lmdb/lmdb-darwin-x64': 2.8.5 + '@lmdb/lmdb-linux-arm': 2.8.5 + '@lmdb/lmdb-linux-arm64': 2.8.5 + '@lmdb/lmdb-linux-x64': 2.8.5 + '@lmdb/lmdb-win32-x64': 2.8.5 + load-yaml-file@0.2.0: dependencies: graceful-fs: 4.2.11 @@ -26080,11 +27412,11 @@ snapshots: lower-case-first@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 lower-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 lowercase-keys@3.0.0: {} @@ -26340,6 +27672,8 @@ snapshots: dependencies: '@types/mdast': 3.0.15 + mdn-data@2.0.14: {} + mdn-data@2.0.28: {} mdn-data@2.0.30: {} @@ -26800,6 +28134,22 @@ snapshots: ms@2.1.3: {} + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.0: + optionalDependencies: + msgpackr-extract: 3.0.3 + msw@2.3.1(typescript@5.5.3): dependencies: '@bundled-es-modules/cookie': 2.0.0 @@ -26981,10 +28331,12 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.3 + tslib: 2.7.0 node-abort-controller@3.1.1: {} + node-addon-api@6.1.0: {} + node-addon-api@7.1.0: {} node-dir@0.1.17: @@ -27011,6 +28363,15 @@ snapshots: node-forge@1.3.1: {} + node-gyp-build-optional-packages@5.1.1: + dependencies: + detect-libc: 2.0.3 + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.3 + optional: true + node-int64@0.4.0: {} node-notifier@10.0.0: @@ -27224,6 +28585,8 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ordered-binary@1.5.2: {} + os-locale@5.0.0: dependencies: execa: 4.1.0 @@ -27306,7 +28669,34 @@ snapshots: param-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 + + parcel@2.12.0(@swc/helpers@0.5.5)(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3): + dependencies: + '@parcel/config-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5)(postcss@8.4.39)(relateurl@0.2.7)(terser@5.31.1)(typescript@5.5.3) + '@parcel/core': 2.12.0(@swc/helpers@0.5.5) + '@parcel/diagnostic': 2.12.0 + '@parcel/events': 2.12.0 + '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/logger': 2.12.0 + '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5))(@swc/helpers@0.5.5) + '@parcel/reporter-cli': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/reporter-tracer': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.5)) + '@parcel/utils': 2.12.0 + chalk: 4.1.2 + commander: 7.2.0 + get-port: 4.2.0 + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss parent-module@1.0.1: dependencies: @@ -27378,7 +28768,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 password-prompt@1.1.3: dependencies: @@ -27390,7 +28780,7 @@ snapshots: path-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 path-equal@1.2.5: {} @@ -27851,6 +29241,23 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + posthtml-parser@0.10.2: + dependencies: + htmlparser2: 7.2.0 + + posthtml-parser@0.11.0: + dependencies: + htmlparser2: 7.2.0 + + posthtml-render@3.0.0: + dependencies: + is-json: 2.0.1 + + posthtml@0.16.6: + dependencies: + posthtml-parser: 0.11.0 + posthtml-render: 3.0.0 + preferred-pm@3.1.4: dependencies: find-up: 5.0.0 @@ -27990,7 +29397,7 @@ snapshots: pvtsutils@1.3.5: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 pvutils@1.1.3: {} @@ -28091,6 +29498,8 @@ snapshots: '@babel/runtime': 7.24.7 react: 18.3.1 + react-error-overlay@6.0.9: {} + react-fast-compare@2.0.4: {} react-hook-form@7.52.1(react@18.3.1): @@ -28138,6 +29547,8 @@ snapshots: react-refresh@0.14.2: {} + react-refresh@0.9.0: {} + react-remove-scroll-bar@2.3.4(@types/react@18.3.3)(react@18.3.1): dependencies: react: 18.3.1 @@ -28150,7 +29561,7 @@ snapshots: dependencies: react: 18.3.1 react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: '@types/react': 18.3.3 @@ -28193,7 +29604,7 @@ snapshots: get-nonce: 1.0.1 invariant: 2.2.4 react: 18.3.1 - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: '@types/react': 18.3.3 @@ -28271,7 +29682,7 @@ snapshots: ast-types: 0.15.2 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.6.3 + tslib: 2.7.0 recast@0.23.3: dependencies: @@ -28279,7 +29690,7 @@ snapshots: ast-types: 0.16.1 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.6.3 + tslib: 2.7.0 recast@0.23.9: dependencies: @@ -28287,7 +29698,7 @@ snapshots: esprima: 4.0.1 source-map: 0.6.1 tiny-invariant: 1.3.3 - tslib: 2.6.3 + tslib: 2.7.0 rechoir@0.6.2: dependencies: @@ -28748,7 +30159,7 @@ snapshots: sentence-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 upper-case-first: 2.0.2 serialize-javascript@6.0.2: @@ -28919,7 +30330,7 @@ snapshots: snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 sonic-boom@3.3.0: dependencies: @@ -28980,10 +30391,12 @@ snapshots: sponge-case@1.0.1: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 sprintf-js@1.0.3: {} + srcset@4.0.0: {} + sshpk@1.17.0: dependencies: asn1: 0.2.6 @@ -28996,6 +30409,8 @@ snapshots: safer-buffer: 2.1.2 tweetnacl: 0.14.5 + stable@0.1.8: {} + stackback@0.0.2: {} stackframe@1.3.4: {} @@ -29230,6 +30645,16 @@ snapshots: svg-parser@2.0.4: {} + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.1 + stable: 0.1.8 + svgo@3.0.2: dependencies: '@trysound/sax': 0.2.0 @@ -29241,7 +30666,7 @@ snapshots: swap-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 swc-loader@0.2.6(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack@5.92.1(@swc/core@1.6.13(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack@5.92.1))): dependencies: @@ -29256,13 +30681,13 @@ snapshots: synckit@0.8.8: dependencies: '@pkgr/core': 0.1.1 - tslib: 2.6.3 + tslib: 2.7.0 tabbable@6.1.1: {} tailwind-merge@2.4.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))): dependencies: tailwindcss: 3.4.4(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3)) @@ -29397,6 +30822,8 @@ snapshots: throwback@4.1.0: {} + timsort@0.3.0: {} + tiny-case@1.0.3: {} tiny-invariant@1.3.1: {} @@ -29413,7 +30840,7 @@ snapshots: title-case@3.0.3: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 title@3.5.3: dependencies: @@ -29946,11 +31373,11 @@ snapshots: upper-case-first@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 upper-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 uri-js@4.4.1: dependencies: @@ -29973,14 +31400,14 @@ snapshots: use-callback-ref@1.3.0(@types/react@18.3.3)(react@18.3.1): dependencies: react: 18.3.1 - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: '@types/react': 18.3.3 use-callback-ref@1.3.2(@types/react@18.3.3)(react@18.3.1): dependencies: react: 18.3.1 - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: '@types/react': 18.3.3 @@ -30011,7 +31438,7 @@ snapshots: dependencies: detect-node-es: 1.1.0 react: 18.3.1 - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: '@types/react': 18.3.3 @@ -30031,6 +31458,8 @@ snapshots: utila@0.4.0: {} + utility-types@3.11.0: {} + utils-merge@1.0.1: {} uuid@3.4.0: {} @@ -30105,13 +31534,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1): + vite-node@1.6.0(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - '@types/node' - less @@ -30122,13 +31551,13 @@ snapshots: - supports-color - terser - vite-node@2.0.1(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1): + vite-node@2.0.1(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1): dependencies: cac: 6.7.14 debug: 4.3.5(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - '@types/node' - less @@ -30139,18 +31568,18 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)): + vite-tsconfig-paths@4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1)): dependencies: debug: 4.3.5(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.1(typescript@5.5.3) optionalDependencies: - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) transitivePeerDependencies: - supports-color - typescript - vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1): + vite@5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1): dependencies: esbuild: 0.21.5 postcss: 8.4.39 @@ -30158,10 +31587,11 @@ snapshots: optionalDependencies: '@types/node': 20.14.10 fsevents: 2.3.3 + lightningcss: 1.27.0 sass: 1.77.6 terser: 5.31.1 - vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(sass@1.77.6)(terser@5.31.1): + vitest@2.0.1(@types/node@20.14.10)(happy-dom@14.12.3)(jsdom@24.1.0)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.1 @@ -30178,8 +31608,8 @@ snapshots: std-env: 3.7.0 tinybench: 2.8.0 tinypool: 1.0.0 - vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) - vite-node: 2.0.1(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1) + vite: 5.3.3(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) + vite-node: 2.0.1(@types/node@20.14.10)(lightningcss@1.27.0)(sass@1.77.6)(terser@5.31.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.14.10 @@ -30245,6 +31675,8 @@ snapshots: dependencies: defaults: 1.0.4 + weak-lru-cache@1.2.2: {} + web-ext@7.6.2: dependencies: '@babel/runtime': 7.21.0 @@ -30301,7 +31733,7 @@ snapshots: '@peculiar/json-schema': 1.1.12 asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.6.3 + tslib: 2.7.0 webextension-polyfill@0.10.0: {} diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index 998ed2b7c4f93..6c4214218dc47 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/create-dapp +## 0.3.26 + +### Patch Changes + +- @mysten/dapp-kit@0.14.26 + ## 0.3.25 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index 4c715a8ff346d..3c42d90b5fbb7 100644 --- a/sdk/create-dapp/package.json +++ b/sdk/create-dapp/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "A CLI for creating new Sui dApps", "homepage": "https://sdk.mystenlabs.com", - "version": "0.3.25", + "version": "0.3.26", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/dapp-kit/CHANGELOG.md b/sdk/dapp-kit/CHANGELOG.md index 0f2751f9aeae0..e8180e45163a7 100644 --- a/sdk/dapp-kit/CHANGELOG.md +++ b/sdk/dapp-kit/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/dapp-kit +## 0.14.26 + +### Patch Changes + +- Updated dependencies [af39b6a] + - @mysten/zksend@0.11.7 + ## 0.14.25 ### Patch Changes diff --git a/sdk/dapp-kit/package.json b/sdk/dapp-kit/package.json index 23b4ac56ff4b1..ce61683b8b08b 100644 --- a/sdk/dapp-kit/package.json +++ b/sdk/dapp-kit/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "A collection of React hooks and components for interacting with the Sui blockchain and wallets.", "homepage": "https://sdk.mystenlabs.com/typescript", - "version": "0.14.25", + "version": "0.14.26", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index 7b2ffc78ebb10..3de7cb9584375 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,35 @@ # @mysten/deepbook-v3 +## 0.11.0 + +### Minor Changes + +- 7b8e8ad: Mainnet pool packages + +## 0.10.0 + +### Minor Changes + +- 23c3a3a: DEEP Mainnet Redeploy + +## 0.9.0 + +### Minor Changes + +- 89f2e59: Mainnet packages + +## 0.8.5 + +### Patch Changes + +- c0fb6d6: Patch ID and bug fix + +## 0.8.4 + +### Patch Changes + +- 5df4e5e: Test Mainnet Packages + ## 0.8.3 ### Patch Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index 9de7dad8c5676..d7a05fea6753a 100644 --- a/sdk/deepbook-v3/package.json +++ b/sdk/deepbook-v3/package.json @@ -2,7 +2,7 @@ "name": "@mysten/deepbook-v3", "author": "Mysten Labs ", "description": "Sui Deepbook SDK", - "version": "0.8.3", + "version": "0.11.0", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/deepbook-v3/src/utils/constants.ts b/sdk/deepbook-v3/src/utils/constants.ts index dd56289d44a50..95b9de6b6c0e1 100644 --- a/sdk/deepbook-v3/src/utils/constants.ts +++ b/sdk/deepbook-v3/src/utils/constants.ts @@ -18,9 +18,9 @@ export const testnetPackageIds = { } satisfies DeepbookPackageIds; export const mainnetPackageIds = { - DEEPBOOK_PACKAGE_ID: '', - REGISTRY_ID: '', - DEEP_TREASURY_ID: '', + DEEPBOOK_PACKAGE_ID: '0x2c8d603bc51326b8c13cef9dd07031a408a48dddb541963357661df5d3204809', + REGISTRY_ID: '0xaf16199a2dff736e9f07a845f23c5da6df6f756eddb631aed9d24a93efc4549d', + DEEP_TREASURY_ID: '0x032abf8948dda67a271bcc18e776dbbcfb0d58c8d288a700ff0d5521e57a1ffe', }; export const testnetCoins: CoinMap = { @@ -60,7 +60,7 @@ export const mainnetCoins: CoinMap = { USDC: { address: `0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7`, type: `0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC`, - scalar: 6, + scalar: 1000000, }, WUSDC: { address: `0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf`, @@ -72,10 +72,15 @@ export const mainnetCoins: CoinMap = { type: `0xaf8cd5edc19c4512f4259f0bee101a40d41ebed738ade5874359610ef8eeced5::coin::COIN`, scalar: 100000000, }, + BETH: { + address: `0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29`, + type: `0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH`, + scalar: 100000000, + }, WBTC: { address: `0x027792d9fed7f9844eb4839566001bb6f6cb4804f66aa2da6fe1ee242d896881`, type: `0x027792d9fed7f9844eb4839566001bb6f6cb4804f66aa2da6fe1ee242d896881::coin::COIN`, - scalar: 0, + scalar: 100000000, }, WUSDT: { address: `0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c`, @@ -109,28 +114,33 @@ export const testnetPools: PoolMap = { export const mainnetPools: PoolMap = { DEEP_SUI: { - address: ``, + address: `0xb663828d6217467c8a1838a03793da896cbe745b150ebd57d82f814ca579fc22`, baseCoin: 'DEEP', quoteCoin: 'SUI', }, SUI_USDC: { - address: ``, + address: `0xe05dafb5133bcffb8d59f4e12465dc0e9faeaa05e3e342a08fe135800e3e4407`, baseCoin: 'SUI', quoteCoin: 'USDC', }, DEEP_USDC: { - address: ``, + address: `0xf948981b806057580f91622417534f491da5f61aeaf33d0ed8e69fd5691c95ce`, baseCoin: 'DEEP', quoteCoin: 'USDC', }, - USDT_USDC: { - address: ``, - baseCoin: 'USDT', + WUSDT_USDC: { + address: `0x52f9bf16d9e7eff79da73d5e3dea39fe1ef8c77684bf4ec2c6566b41396404d0`, + baseCoin: 'WUSDT', quoteCoin: 'USDC', }, WUSDC_USDC: { - address: ``, + address: `0xc69f7755fec146583e276a104bcf91e0c9f0cab91dcdb1c202e8d76a5a5a1101`, baseCoin: 'WUSDC', quoteCoin: 'USDC', }, + BETH_USDC: { + address: `0x1109352b9112717bd2a7c3eb9a416fff1ba6951760f5bdd5424cf5e4e5b3e65c`, + baseCoin: 'BETH', + quoteCoin: 'USDC', + }, }; diff --git a/sdk/graphql-transport/CHANGELOG.md b/sdk/graphql-transport/CHANGELOG.md index 0341c4cd3996e..7e16a6714e3b8 100644 --- a/sdk/graphql-transport/CHANGELOG.md +++ b/sdk/graphql-transport/CHANGELOG.md @@ -1,5 +1,22 @@ # @mysten/graphql-transport +## 0.2.23 + +### Patch Changes + +- 5299c18: Update the GraphQL transport to account for the removal of recvAddress and the + introduction of affectedAddress. + +## 0.2.22 + +### Patch Changes + +- af39b6a: Update to reflect GraphQL schema renaming TransactionBlockFilter.signAddress to + .sentAddress. +- 4d63e50: Update GraphQL transport layer to accommodate change in schema +- 2cddd9d: Update the GraphQL transport to account for the removal of recvAddress and the + introduction of affectedAddress. + ## 0.2.21 ### Patch Changes diff --git a/sdk/graphql-transport/package.json b/sdk/graphql-transport/package.json index aabb5462d89a5..ace5216080256 100644 --- a/sdk/graphql-transport/package.json +++ b/sdk/graphql-transport/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/graphql-transport", - "version": "0.2.21", + "version": "0.2.23", "description": "A GraphQL transport to allow SuiClient to work with RPC 2.0", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/graphql-transport/src/generated/queries.ts b/sdk/graphql-transport/src/generated/queries.ts index fea938d8e1923..dc0be45904337 100644 --- a/sdk/graphql-transport/src/generated/queries.ts +++ b/sdk/graphql-transport/src/generated/queries.ts @@ -313,23 +313,12 @@ export type AddressOwner = { /** The possible relationship types for a transaction block: sent, or received. */ export enum AddressTransactionBlockRelationship { /** - * Transactions that sent objects to this address. NOTE: this input filter has been deprecated - * in favor of `AFFECTED`, which offers an easier to understand behavior. - * - * This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - * `AFFECTED` is introduced, whichever is later. + * Transactions that this address was involved in, either as the sender, sponsor, or as the + * owner of some object that was created, modified or transfered. */ - Recv = 'RECV', + Affected = 'AFFECTED', /** Transactions this address has sent. */ - Sent = 'SENT', - /** - * Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - * `SENT` which behaves identically but is named more clearly. Both filters restrict - * transactions by their sender, only, not signers in general. - * - * This filter will be removed with 1.36.0 (2024-10-14). - */ - Sign = 'SIGN' + Sent = 'SENT' } /** System transaction for creating the on-chain state used by zkLogin. */ @@ -1394,6 +1383,25 @@ export type EpochTransactionBlocksArgs = { scanLimit?: InputMaybe; }; +export type EpochConnection = { + __typename?: 'EpochConnection'; + /** A list of edges. */ + edges: Array; + /** A list of nodes. */ + nodes: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + +/** An edge in a connection. */ +export type EpochEdge = { + __typename?: 'EpochEdge'; + /** A cursor for use in pagination */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge */ + node: Epoch; +}; + export type Event = { __typename?: 'Event'; /** The Base64 encoded BCS serialized bytes of the event. */ @@ -1412,6 +1420,12 @@ export type Event = { sendingModule?: Maybe; /** UTC timestamp in milliseconds since epoch (1/1/1970) */ timestamp?: Maybe; + /** + * The transaction block that emitted this event. This information is only available for + * events from indexed transactions, and not from transactions that have just been executed or + * dry-run. + */ + transactionBlock?: Maybe; }; export type EventConnection = { @@ -3278,12 +3292,7 @@ export enum ObjectKind { * The object is loaded from serialized data, such as the contents of a transaction that hasn't * been indexed yet. */ - NotIndexed = 'NOT_INDEXED', - /** - * The object is deleted or wrapped and only partial information can be loaded from the - * indexer. - */ - WrappedOrDeleted = 'WRAPPED_OR_DELETED' + NotIndexed = 'NOT_INDEXED' } /** The object's owner type: Immutable, Shared, Parent, or Address. */ @@ -3520,12 +3529,14 @@ export type PageInfo = { /** * If the object's owner is a Parent, this object is part of a dynamic field (it is the value of - * the dynamic field, or the intermediate Field object itself). Also note that if the owner - * is a parent, then it's guaranteed to be an object. + * the dynamic field, or the intermediate Field object itself), and it is owned by another object. + * + * Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent + * object could be wrapped and therefore not directly accessible. */ export type Parent = { __typename?: 'Parent'; - parent?: Maybe; + parent?: Maybe; }; /** A single transaction, or command, in the programmable transaction block. */ @@ -3717,6 +3728,7 @@ export type Query = { dryRunTransactionBlock: DryRunResult; /** Fetch epoch information by ID (defaults to the latest epoch). */ epoch?: Maybe; + epochs: EpochConnection; /** * Query events that are emitted in the network. * We currently do not support filtering by emitting module and event type @@ -3886,6 +3898,14 @@ export type QueryEpochArgs = { }; +export type QueryEpochsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +}; + + export type QueryEventsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -4930,6 +4950,11 @@ export type TransactionBlockEffectsUnchangedSharedObjectsArgs = { }; export type TransactionBlockFilter = { + /** + * Limit to transactions that interacted with the given address. The address could be a + * sender, sponsor, or recipient of the transaction. + */ + affectedAddress?: InputMaybe; /** Limit to transactions that occured strictly after the given checkpoint. */ afterCheckpoint?: InputMaybe; /** Limit to transactions in the given checkpoint. */ @@ -4959,25 +4984,8 @@ export type TransactionBlockFilter = { inputObject?: InputMaybe; /** An input filter selecting for either system or programmable transactions. */ kind?: InputMaybe; - /** - * Limit to transactions that sent an object to the given address. NOTE: this input filter has - * been deprecated in favor of `affectedAddress` which offers an easier to understand - * behavior. - * - * This filter will be removed with 1.36.0 (2024-10-14), or at least one release after - * `affectedAddress` is introduced, whichever is later. - */ - recvAddress?: InputMaybe; /** Limit to transactions that were sent by the given address. */ sentAddress?: InputMaybe; - /** - * Limit to transactions that were sent by the given address. NOTE: this input filter has been - * deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - * Both filters restrict transactions by their sender, only, not signers in general. - * - * This filter will be removed with 1.36.0 (2024-10-14). - */ - signAddress?: InputMaybe; /** Select transactions by their digest. */ transactionIds?: InputMaybe>; }; @@ -5433,7 +5441,7 @@ export type GetDynamicFieldObjectQueryVariables = Exact<{ }>; -export type GetDynamicFieldObjectQuery = { __typename?: 'Query', owner?: { __typename?: 'Owner', dynamicObjectField?: { __typename?: 'DynamicField', value?: { __typename: 'MoveObject', owner?: { __typename: 'AddressOwner' } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any, digest?: string | null, version: any, storageRebate?: any | null, owner?: { __typename: 'AddressOwner' } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared' } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null } } | null } | null } | null } | { __typename: 'Shared' } | null } | { __typename: 'MoveValue' } | null } | null } | null }; +export type GetDynamicFieldObjectQuery = { __typename?: 'Query', owner?: { __typename?: 'Owner', dynamicObjectField?: { __typename?: 'DynamicField', value?: { __typename: 'MoveObject', owner?: { __typename: 'AddressOwner' } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any, digest?: string | null, version: any, storageRebate?: any | null, owner?: { __typename: 'AddressOwner' } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared' } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null } } | null } | null } | null } | null } | { __typename: 'Shared' } | null } | { __typename: 'MoveValue' } | null } | null } | null }; export type GetDynamicFieldsQueryVariables = Exact<{ parentId: Scalars['SuiAddress']['input']; @@ -5577,7 +5585,7 @@ export type GetOwnedObjectsQueryVariables = Exact<{ }>; -export type GetOwnedObjectsQuery = { __typename?: 'Query', address?: { __typename?: 'Address', objects: { __typename?: 'MoveObjectConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor?: string | null }, nodes: Array<{ __typename?: 'MoveObject', bcs?: any | null, hasPublicTransfer?: boolean, storageRebate?: any | null, digest?: string | null, version: any, objectId: any, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }> } } | null }; +export type GetOwnedObjectsQuery = { __typename?: 'Query', address?: { __typename?: 'Address', objects: { __typename?: 'MoveObjectConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor?: string | null }, nodes: Array<{ __typename?: 'MoveObject', bcs?: any | null, hasPublicTransfer?: boolean, storageRebate?: any | null, digest?: string | null, version: any, objectId: any, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }> } } | null }; export type GetObjectQueryVariables = Exact<{ id: Scalars['SuiAddress']['input']; @@ -5591,7 +5599,7 @@ export type GetObjectQueryVariables = Exact<{ }>; -export type GetObjectQuery = { __typename?: 'Query', object?: { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null } | null }; +export type GetObjectQuery = { __typename?: 'Query', object?: { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null } | null }; export type TryGetPastObjectQueryVariables = Exact<{ id: Scalars['SuiAddress']['input']; @@ -5606,7 +5614,7 @@ export type TryGetPastObjectQueryVariables = Exact<{ }>; -export type TryGetPastObjectQuery = { __typename?: 'Query', current?: { __typename?: 'Object', address: any, version: any } | null, object?: { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null } | null }; +export type TryGetPastObjectQuery = { __typename?: 'Query', current?: { __typename?: 'Object', address: any, version: any } | null, object?: { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null } | null }; export type MultiGetObjectsQueryVariables = Exact<{ ids: Array | Scalars['SuiAddress']['input']; @@ -5622,17 +5630,17 @@ export type MultiGetObjectsQueryVariables = Exact<{ }>; -export type MultiGetObjectsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor?: string | null }, nodes: Array<{ __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }> } }; +export type MultiGetObjectsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor?: string | null }, nodes: Array<{ __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }> } }; -export type Rpc_Object_FieldsFragment = { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }; +export type Rpc_Object_FieldsFragment = { __typename?: 'Object', version: any, storageRebate?: any | null, digest?: string | null, objectId: any, asMoveObject?: { __typename?: 'MoveObject', hasPublicTransfer: boolean, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }; -export type Rpc_Move_Object_FieldsFragment = { __typename?: 'MoveObject', bcs?: any | null, hasPublicTransfer?: boolean, storageRebate?: any | null, digest?: string | null, version: any, objectId: any, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }; +export type Rpc_Move_Object_FieldsFragment = { __typename?: 'MoveObject', bcs?: any | null, hasPublicTransfer?: boolean, storageRebate?: any | null, digest?: string | null, version: any, objectId: any, contents?: { __typename?: 'MoveValue', data: any, bcs: any, type: { __typename?: 'MoveType', repr: string, layout?: any | null, signature: any } } | null, owner?: { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null } | { __typename: 'Immutable' } | { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null } | { __typename: 'Shared', initialSharedVersion: any } | null, previousTransactionBlock?: { __typename?: 'TransactionBlock', digest?: string | null } | null, display?: Array<{ __typename?: 'DisplayEntry', key: string, value?: string | null, error?: string | null }> | null }; type Rpc_Object_Owner_Fields_AddressOwner_Fragment = { __typename: 'AddressOwner', owner?: { __typename?: 'Owner', asObject?: { __typename?: 'Object', address: any } | null, asAddress?: { __typename?: 'Address', address: any } | null } | null }; type Rpc_Object_Owner_Fields_Immutable_Fragment = { __typename: 'Immutable' }; -type Rpc_Object_Owner_Fields_Parent_Fragment = { __typename: 'Parent', parent?: { __typename?: 'Object', address: any } | null }; +type Rpc_Object_Owner_Fields_Parent_Fragment = { __typename: 'Parent', parent?: { __typename?: 'Owner', address: any } | null }; type Rpc_Object_Owner_Fields_Shared_Fragment = { __typename: 'Shared', initialSharedVersion: any }; @@ -7138,30 +7146,32 @@ export const GetDynamicFieldObjectDocument = new TypedDocumentString(` __typename ... on Parent { parent { - address - digest - version - storageRebate - owner { - __typename - ... on Parent { - parent { - address + asObject { + address + digest + version + storageRebate + owner { + __typename + ... on Parent { + parent { + address + } } } - } - previousTransactionBlock { - digest - } - asMoveObject { - contents { - data - type { - repr - layout + previousTransactionBlock { + digest + } + asMoveObject { + contents { + data + type { + repr + layout + } } + hasPublicTransfer } - hasPublicTransfer } } } diff --git a/sdk/graphql-transport/src/mappers/transaction-block.ts b/sdk/graphql-transport/src/mappers/transaction-block.ts index ed38605e9a48c..44916cfe1cbea 100644 --- a/sdk/graphql-transport/src/mappers/transaction-block.ts +++ b/sdk/graphql-transport/src/mappers/transaction-block.ts @@ -73,7 +73,8 @@ export function mapGraphQLTransactionBlockToRpcTransactionBlock( transaction: transactionBlock.rawTransaction && mapTransactionBlockToInput( - bcs.SenderSignedData.parse(fromBase64(transactionBlock.rawTransaction))[0], + bcs.TransactionData.parse(fromBase64(transactionBlock.rawTransaction)), + transactionBlock.signatures, ), } : {}), @@ -169,10 +170,12 @@ function mapObjectChanges( } export function mapTransactionBlockToInput( - data: typeof bcs.SenderSignedTransaction.$inferType, + data: typeof bcs.TransactionData.$inferType, + signatures: any[] | null | undefined, ): SuiTransactionBlock | null { - const txData = data.intentMessage.value.V1; - + const txData = data.V1; + console.log('Signatures:', signatures); + const sigs: string[] = (signatures ?? []).filter((sig): sig is string => typeof sig === 'string'); const programableTransaction = 'ProgrammableTransaction' in txData.kind ? txData.kind.ProgrammableTransaction : null; @@ -181,7 +184,7 @@ export function mapTransactionBlockToInput( } return { - txSignatures: data.txSignatures, + txSignatures: sigs, data: { gasData: { budget: txData.gasData.budget, diff --git a/sdk/graphql-transport/src/methods.ts b/sdk/graphql-transport/src/methods.ts index 86de1d7bbd326..982940aeaefe5 100644 --- a/sdk/graphql-transport/src/methods.ts +++ b/sdk/graphql-transport/src/methods.ts @@ -622,8 +622,8 @@ export const RPC_METHODS: { : undefined, inputObject: 'InputObject' in filter ? filter.InputObject : undefined, changedObject: 'ChangedObject' in filter ? filter.ChangedObject : undefined, - signAddress: 'FromAddress' in filter ? filter.FromAddress : undefined, - recvAddress: 'ToAddress' in filter ? filter.ToAddress : undefined, + sentAddress: 'FromAddress' in filter ? filter.FromAddress : undefined, + affectedAddress: 'ToAddress' in filter ? filter.ToAddress : undefined, kind: 'TransactionKind' in filter ? filter.TransactionKind === 'ProgrammableTransaction' @@ -1020,7 +1020,7 @@ export const RPC_METHODS: { (data) => { return data.owner?.dynamicObjectField?.value?.__typename === 'MoveObject' ? data.owner.dynamicObjectField.value.owner?.__typename === 'Parent' - ? data.owner.dynamicObjectField.value.owner.parent + ? data.owner.dynamicObjectField.value.owner.parent?.asObject : undefined : undefined; }, diff --git a/sdk/graphql-transport/src/queries/getDynamicFieldObject.graphql b/sdk/graphql-transport/src/queries/getDynamicFieldObject.graphql index a613502fd3d5e..aca4e5eca8f7e 100644 --- a/sdk/graphql-transport/src/queries/getDynamicFieldObject.graphql +++ b/sdk/graphql-transport/src/queries/getDynamicFieldObject.graphql @@ -17,30 +17,32 @@ query getDynamicFieldObject($parentId: SuiAddress!, $name: DynamicFieldName!) { __typename ... on Parent { parent { - address - digest - version - storageRebate - owner { - __typename - ... on Parent { - parent { - address + asObject { + address + digest + version + storageRebate + owner { + __typename + ... on Parent { + parent { + address + } } } - } - previousTransactionBlock { - digest - } - asMoveObject { - contents { - data - type { - repr - layout + previousTransactionBlock { + digest + } + asMoveObject { + contents { + data + type { + repr + layout + } } + hasPublicTransfer } - hasPublicTransfer } } } diff --git a/sdk/graphql-transport/test/compatability.test.ts b/sdk/graphql-transport/test/compatability.test.ts index d8a0fbee58d81..a017d9f71a85a 100644 --- a/sdk/graphql-transport/test/compatability.test.ts +++ b/sdk/graphql-transport/test/compatability.test.ts @@ -364,7 +364,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); @@ -380,7 +380,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); @@ -397,7 +397,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, })) as SuiTransactionBlockResponse & { rawEffects: unknown }; const graphQLTransactionBlock = await graphQLClient!.getTransactionBlock({ @@ -408,7 +408,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); @@ -426,7 +426,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); const [graphQLTransactionBlock] = await graphQLClient!.multiGetTransactionBlocks({ @@ -439,7 +439,7 @@ describe('GraphQL SuiClient compatibility', () => { // TODO inputs missing valueType showInput: false, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); @@ -589,7 +589,7 @@ describe('GraphQL SuiClient compatibility', () => { showEvents: true, showInput: true, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, }); @@ -604,7 +604,7 @@ describe('GraphQL SuiClient compatibility', () => { showEvents: true, showInput: true, showObjectChanges: true, - showRawInput: true, + showRawInput: false, }, })) as SuiTransactionBlockResponse & { rawEffects: unknown }; diff --git a/sdk/zksend/CHANGELOG.md b/sdk/zksend/CHANGELOG.md index 702428db32abf..38269a00f083d 100644 --- a/sdk/zksend/CHANGELOG.md +++ b/sdk/zksend/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/zksend +## 0.11.7 + +### Patch Changes + +- af39b6a: Update to reflect GraphQL schema renaming TransactionBlockFilter.signAddress to + .sentAddress. + ## 0.11.6 ### Patch Changes diff --git a/sdk/zksend/package.json b/sdk/zksend/package.json index 55fdc0bdcceaf..b6f1222ad68d2 100644 --- a/sdk/zksend/package.json +++ b/sdk/zksend/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zksend", - "version": "0.11.6", + "version": "0.11.7", "description": "TODO: Write Description", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zksend/src/links/list-created-links.ts b/sdk/zksend/src/links/list-created-links.ts index aa8aa5864ab6f..2e8248f824e7c 100644 --- a/sdk/zksend/src/links/list-created-links.ts +++ b/sdk/zksend/src/links/list-created-links.ts @@ -16,7 +16,7 @@ const ListCreatedLinksQuery = graphql(` transactionBlocks( last: 10 before: $cursor - filter: { signAddress: $address, function: $function, kind: PROGRAMMABLE_TX } + filter: { sentAddress: $address, function: $function, kind: PROGRAMMABLE_TX } ) { pageInfo { startCursor diff --git a/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs index 7038c6a46ac6d..18b20ada7b3dc 100644 --- a/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v0/sui-adapter/src/programmable_transactions/execution.rs @@ -743,14 +743,9 @@ mod checked { }; let compatibility = Compatibility { - check_datatype_and_pub_function_linking: true, check_datatype_layout: true, - check_friend_linking: false, check_private_entry_linking: false, disallowed_new_abilities, - disallow_change_datatype_type_params: protocol_config - .disallow_change_struct_type_params_on_upgrade(), - disallow_new_variants: true, }; compatibility.check(cur_module, new_module) diff --git a/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs index 2b66c77725e57..e0ec4766306f7 100644 --- a/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v1/sui-adapter/src/programmable_transactions/execution.rs @@ -681,15 +681,7 @@ mod checked { UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module), UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module), UpgradePolicy::Compatible => { - let compatibility = Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }; + let compatibility = Compatibility::upgrade_check(); compatibility.check(cur_module, new_module) } diff --git a/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs b/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs index ef52166d99d22..ac751d9d4a72c 100644 --- a/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs +++ b/sui-execution/v2/sui-adapter/src/programmable_transactions/execution.rs @@ -687,15 +687,7 @@ mod checked { UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module), UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module), UpgradePolicy::Compatible => { - let compatibility = Compatibility { - check_datatype_and_pub_function_linking: true, - check_datatype_layout: true, - check_friend_linking: false, - check_private_entry_linking: false, - disallowed_new_abilities: AbilitySet::ALL, - disallow_change_datatype_type_params: true, - disallow_new_variants: true, - }; + let compatibility = Compatibility::upgrade_check(); compatibility.check(cur_module, new_module) }